1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3 * Copyright (c) 1994 by the University of Southern California
4 *
5 * EXPORT OF THIS SOFTWARE from the United States of America may
6 * require a specific license from the United States Government.
7 * It is the responsibility of any person or organization contemplating
8 * export to obtain such a license before exporting.
9 *
10 * WITHIN THAT CONSTRAINT, permission to copy, modify, and distribute
11 * this software and its documentation in source and binary forms is
12 * hereby granted, provided that any documentation or other materials
13 * related to such distribution or use acknowledge that the software
14 * was developed by the University of Southern California.
15 *
16 * DISCLAIMER OF WARRANTY. THIS SOFTWARE IS PROVIDED "AS IS". The
17 * University of Southern California MAKES NO REPRESENTATIONS OR
18 * WARRANTIES, EXPRESS OR IMPLIED. By way of example, but not
19 * limitation, the University of Southern California MAKES NO
20 * REPRESENTATIONS OR WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY
21 * PARTICULAR PURPOSE. The University of Southern
22 * California shall not be held liable for any liability nor for any
23 * direct, indirect, or consequential damages with respect to any
24 * claim by the user or distributor of the ksu software.
25 *
26 * KSU was written by: Ari Medvinsky, ari@isi.edu
27 */
28
29 #include "ksu.h"
30 #include "k5-base64.h"
31 #include "adm_proto.h"
32 #include <sys/types.h>
33 #include <sys/stat.h>
34
35 /******************************************************************
36 krb5_cache_copy
37
38 gets rid of any expired tickets in the secondary cache,
39 copies the default cache into the secondary cache,
40
41 ************************************************************************/
42
43 void show_credential();
44
45 /* modifies only the cc_other, the algorithm may look a bit funny,
46 but I had to do it this way, since remove function did not come
47 with k5 beta 3 release.
48 */
49
krb5_ccache_copy(context,cc_def,target_principal,cc_target,restrict_creds,primary_principal,stored)50 krb5_error_code krb5_ccache_copy(context, cc_def, target_principal, cc_target,
51 restrict_creds, primary_principal, stored)
52 /* IN */
53 krb5_context context;
54 krb5_ccache cc_def;
55 krb5_principal target_principal;
56 krb5_ccache cc_target;
57 krb5_boolean restrict_creds;
58 krb5_principal primary_principal;
59 /* OUT */
60 krb5_boolean *stored;
61 {
62 int i=0;
63 krb5_error_code retval=0;
64 krb5_creds ** cc_def_creds_arr = NULL;
65 krb5_creds ** cc_other_creds_arr = NULL;
66
67 if (ks_ccache_is_initialized(context, cc_def)) {
68 if((retval = krb5_get_nonexp_tkts(context,cc_def,&cc_def_creds_arr))){
69 return retval;
70 }
71 }
72
73 retval = krb5_cc_initialize(context, cc_target, target_principal);
74 if (retval)
75 return retval;
76
77 if (restrict_creds) {
78 retval = krb5_store_some_creds(context, cc_target, cc_def_creds_arr,
79 cc_other_creds_arr, primary_principal,
80 stored);
81 } else {
82 *stored = krb5_find_princ_in_cred_list(context, cc_def_creds_arr,
83 primary_principal);
84 retval = krb5_store_all_creds(context, cc_target, cc_def_creds_arr,
85 cc_other_creds_arr);
86 }
87
88 if (cc_def_creds_arr){
89 while (cc_def_creds_arr[i]){
90 krb5_free_creds(context, cc_def_creds_arr[i]);
91 i++;
92 }
93 }
94
95 i=0;
96
97 if(cc_other_creds_arr){
98 while (cc_other_creds_arr[i]){
99 krb5_free_creds(context, cc_other_creds_arr[i]);
100 i++;
101 }
102 }
103
104 return retval;
105 }
106
107
krb5_store_all_creds(context,cc,creds_def,creds_other)108 krb5_error_code krb5_store_all_creds(context, cc, creds_def, creds_other)
109 krb5_context context;
110 krb5_ccache cc;
111 krb5_creds **creds_def;
112 krb5_creds **creds_other;
113 {
114
115 int i = 0;
116 krb5_error_code retval = 0;
117 krb5_creds ** temp_creds= NULL;
118
119
120 if ((creds_def == NULL) && (creds_other == NULL))
121 return 0;
122
123 if ((creds_def == NULL) && (creds_other != NULL))
124 temp_creds = creds_other;
125
126 if ((creds_def != NULL) && (creds_other == NULL))
127 temp_creds = creds_def;
128
129
130 if (temp_creds){
131 while(temp_creds[i]){
132 if ((retval= krb5_cc_store_cred(context, cc,
133 temp_creds[i]))){
134 return retval;
135 }
136 i++;
137 }
138 }
139 else { /* both arrays have elements in them */
140
141 return KRB5KRB_ERR_GENERIC;
142
143 /************ while(creds_other[i]){
144 cmp = FALSE;
145 j = 0;
146 while(creds_def[j]){
147 cmp = compare_creds(creds_other[i],creds_def[j]);
148
149 if( cmp == TRUE) break;
150
151 j++;
152 }
153 if (cmp == FALSE){
154 if (retval= krb5_cc_store_cred(context, cc,
155 creds_other[i])){
156 return retval;
157 }
158 }
159 i ++;
160 }
161
162 i=0;
163 while(creds_def[i]){
164 if (retval= krb5_cc_store_cred(context, cc,
165 creds_def[i])){
166 return retval;
167 }
168 i++;
169 }
170
171 **************/
172 }
173 return 0;
174 }
175
compare_creds(context,cred1,cred2)176 krb5_boolean compare_creds(context, cred1, cred2)
177 krb5_context context;
178 krb5_creds *cred1;
179 krb5_creds *cred2;
180 {
181 krb5_boolean retval;
182
183 retval = krb5_principal_compare (context, cred1->client, cred2->client);
184
185 if (retval == TRUE)
186 retval = krb5_principal_compare (context, cred1->server, cred2->server);
187
188 return retval;
189 }
190
191
192
193
krb5_get_nonexp_tkts(context,cc,creds_array)194 krb5_error_code krb5_get_nonexp_tkts(context, cc, creds_array)
195 krb5_context context;
196 krb5_ccache cc;
197 krb5_creds ***creds_array;
198 {
199
200 krb5_creds creds, temp_tktq, temp_tkt;
201 krb5_creds **temp_creds;
202 krb5_error_code retval=0;
203 krb5_cc_cursor cur;
204 int count = 0;
205 int chunk_count = 1;
206
207 if ( ! ( temp_creds = (krb5_creds **) malloc( CHUNK * sizeof(krb5_creds *)))){
208 return ENOMEM;
209 }
210
211
212 memset(&temp_tktq, 0, sizeof(temp_tktq));
213 memset(&temp_tkt, 0, sizeof(temp_tkt));
214 memset(&creds, 0, sizeof(creds));
215
216 /* initialize the cursor */
217 if ((retval = krb5_cc_start_seq_get(context, cc, &cur))) {
218 return retval;
219 }
220
221 while (!(retval = krb5_cc_next_cred(context, cc, &cur, &creds))){
222
223 if (!krb5_is_config_principal(context, creds.server) &&
224 (retval = krb5_check_exp(context, creds.times))){
225 if (retval != KRB5KRB_AP_ERR_TKT_EXPIRED){
226 return retval;
227 }
228 if (auth_debug){
229 fprintf(stderr,"krb5_ccache_copy: CREDS EXPIRED:\n");
230 fputs(" Valid starting Expires Service principal\n",stdout);
231 show_credential(context, &creds, cc);
232 fprintf(stderr,"\n");
233 }
234 }
235 else { /* these credentials didn't expire */
236
237 if ((retval = krb5_copy_creds(context, &creds,
238 &temp_creds[count]))){
239 return retval;
240 }
241 count ++;
242
243 if (count == (chunk_count * CHUNK -1)){
244 chunk_count ++;
245 if (!(temp_creds = (krb5_creds **) realloc(temp_creds,
246 chunk_count * CHUNK * sizeof(krb5_creds *)))){
247 return ENOMEM;
248 }
249 }
250 }
251
252 }
253
254 temp_creds[count] = NULL;
255 *creds_array = temp_creds;
256
257 if (retval == KRB5_CC_END) {
258 retval = krb5_cc_end_seq_get(context, cc, &cur);
259 }
260
261 return retval;
262
263 }
264
265
krb5_check_exp(context,tkt_time)266 krb5_error_code krb5_check_exp(context, tkt_time)
267 krb5_context context;
268 krb5_ticket_times tkt_time;
269 {
270 krb5_error_code retval =0;
271 krb5_timestamp currenttime;
272
273 if ((retval = krb5_timeofday (context, ¤ttime))){
274 return retval;
275 }
276 if (auth_debug){
277 fprintf(stderr,"krb5_check_exp: the krb5_clockskew is %d \n",
278 context->clockskew);
279
280 fprintf(stderr,"krb5_check_exp: currenttime - endtime %d \n",
281 ts_delta(currenttime, tkt_time.endtime));
282
283 }
284
285 if (ts_after(currenttime, ts_incr(tkt_time.endtime, context->clockskew))) {
286 retval = KRB5KRB_AP_ERR_TKT_EXPIRED ;
287 return retval;
288 }
289
290 return 0;
291 }
292
293
flags_string(cred)294 char *flags_string(cred)
295 krb5_creds *cred;
296 {
297 static char buf[32];
298 int i = 0;
299
300 if (cred->ticket_flags & TKT_FLG_FORWARDABLE)
301 buf[i++] = 'F';
302 if (cred->ticket_flags & TKT_FLG_FORWARDED)
303 buf[i++] = 'f';
304 if (cred->ticket_flags & TKT_FLG_PROXIABLE)
305 buf[i++] = 'P';
306 if (cred->ticket_flags & TKT_FLG_PROXY)
307 buf[i++] = 'p';
308 if (cred->ticket_flags & TKT_FLG_MAY_POSTDATE)
309 buf[i++] = 'D';
310 if (cred->ticket_flags & TKT_FLG_POSTDATED)
311 buf[i++] = 'd';
312 if (cred->ticket_flags & TKT_FLG_INVALID)
313 buf[i++] = 'i';
314 if (cred->ticket_flags & TKT_FLG_RENEWABLE)
315 buf[i++] = 'R';
316 if (cred->ticket_flags & TKT_FLG_INITIAL)
317 buf[i++] = 'I';
318 if (cred->ticket_flags & TKT_FLG_HW_AUTH)
319 buf[i++] = 'H';
320 if (cred->ticket_flags & TKT_FLG_PRE_AUTH)
321 buf[i++] = 'A';
322 buf[i] = '\0';
323 return(buf);
324 }
325
printtime(krb5_timestamp ts)326 void printtime(krb5_timestamp ts)
327 {
328 char fmtbuf[18], fill = ' ';
329
330 if (!krb5_timestamp_to_sfstring(ts, fmtbuf, sizeof(fmtbuf), &fill))
331 printf("%s", fmtbuf);
332 }
333
334
335 krb5_error_code
krb5_get_login_princ(luser,princ_list)336 krb5_get_login_princ(luser, princ_list)
337 const char *luser;
338 char ***princ_list;
339 {
340 struct stat sbuf;
341 struct passwd *pwd;
342 char pbuf[MAXPATHLEN];
343 FILE *fp;
344 char * linebuf;
345 char *newline;
346 int gobble, result;
347 char ** buf_out;
348 struct stat st_temp;
349 int count = 0, chunk_count = 1;
350
351 /* no account => no access */
352
353 if ((pwd = getpwnam(luser)) == NULL) {
354 return 0;
355 }
356 result = snprintf(pbuf, sizeof(pbuf), "%s/.k5login", pwd->pw_dir);
357 if (SNPRINTF_OVERFLOW(result, sizeof(pbuf))) {
358 fprintf(stderr, _("home directory path for %s too long\n"), luser);
359 exit (1);
360 }
361
362 if (stat(pbuf, &st_temp)) { /* not accessible */
363 return 0;
364 }
365
366
367 /* open ~/.k5login */
368 if ((fp = fopen(pbuf, "r")) == NULL) {
369 return 0;
370 }
371 /*
372 * For security reasons, the .k5login file must be owned either by
373 * the user himself, or by root. Otherwise, don't grant access.
374 */
375 if (fstat(fileno(fp), &sbuf)) {
376 fclose(fp);
377 return 0;
378 }
379 if ((sbuf.st_uid != pwd->pw_uid) && sbuf.st_uid) {
380 fclose(fp);
381 return 0;
382 }
383
384 /* check each line */
385
386
387 if( !(linebuf = (char *) calloc (BUFSIZ, sizeof(char)))) return ENOMEM;
388
389 if (!(buf_out = (char **) malloc( CHUNK * sizeof(char *)))) return ENOMEM;
390
391 while ( fgets(linebuf, BUFSIZ, fp) != NULL) {
392 /* null-terminate the input string */
393 linebuf[BUFSIZ-1] = '\0';
394 newline = NULL;
395 /* nuke the newline if it exists */
396 if ((newline = strchr(linebuf, '\n')))
397 *newline = '\0';
398
399 buf_out[count] = linebuf;
400 count ++;
401
402 if (count == (chunk_count * CHUNK -1)){
403 chunk_count ++;
404 if (!(buf_out = (char **) realloc(buf_out,
405 chunk_count * CHUNK * sizeof(char *)))){
406 return ENOMEM;
407 }
408 }
409
410 /* clean up the rest of the line if necessary */
411 if (!newline)
412 while (((gobble = getc(fp)) != EOF) && gobble != '\n');
413
414 if( !(linebuf = (char *) calloc (BUFSIZ, sizeof(char)))) return ENOMEM;
415 }
416
417 buf_out[count] = NULL;
418 *princ_list = buf_out;
419 fclose(fp);
420 return 0;
421 }
422
423
424
425 void
show_credential(context,cred,cc)426 show_credential(context, cred, cc)
427 krb5_context context;
428 krb5_creds *cred;
429 krb5_ccache cc;
430 {
431 krb5_error_code retval;
432 char *name, *sname, *flags;
433 int first = 1;
434 krb5_principal princ;
435 char * defname;
436 int show_flags =1;
437
438 retval = krb5_unparse_name(context, cred->client, &name);
439 if (retval) {
440 com_err(prog_name, retval, _("while unparsing client name"));
441 return;
442 }
443 retval = krb5_unparse_name(context, cred->server, &sname);
444 if (retval) {
445 com_err(prog_name, retval, _("while unparsing server name"));
446 free(name);
447 return;
448 }
449
450 if ((retval = krb5_cc_get_principal(context, cc, &princ))) {
451 com_err(prog_name, retval, _("while retrieving principal name"));
452 return;
453 }
454 if ((retval = krb5_unparse_name(context, princ, &defname))) {
455 com_err(prog_name, retval, _("while unparsing principal name"));
456 return;
457 }
458
459 if (!cred->times.starttime)
460 cred->times.starttime = cred->times.authtime;
461
462 printtime(cred->times.starttime);
463 putchar(' '); putchar(' ');
464 printtime(cred->times.endtime);
465 putchar(' '); putchar(' ');
466
467 printf("%s\n", sname);
468
469 if (strcmp(name, defname)) {
470 printf(_("\tfor client %s"), name);
471 first = 0;
472 }
473
474 if (cred->times.renew_till) {
475 if (first)
476 fputs("\t",stdout);
477 else
478 fputs(", ",stdout);
479 fputs(_("renew until "), stdout);
480 printtime(cred->times.renew_till);
481 }
482 if (show_flags) {
483 flags = flags_string(cred);
484 if (flags && *flags) {
485 if (first)
486 fputs("\t",stdout);
487 else
488 fputs(", ",stdout);
489 printf(_("Flags: %s"), flags);
490 first = 0;
491 }
492 }
493 putchar('\n');
494 free(name);
495 free(sname);
496 }
497
498 /* Create a random string suitable for a filename extension. */
499 krb5_error_code
gen_sym(krb5_context context,char ** sym_out)500 gen_sym(krb5_context context, char **sym_out)
501 {
502 krb5_error_code retval;
503 char bytes[6], *p, *sym;
504 krb5_data data = make_data(bytes, sizeof(bytes));
505
506 *sym_out = NULL;
507 retval = krb5_c_random_make_octets(context, &data);
508 if (retval)
509 return retval;
510 sym = k5_base64_encode(data.data, data.length);
511 if (sym == NULL)
512 return ENOMEM;
513 /* Tweak the output alphabet just a bit. */
514 while ((p = strchr(sym, '/')) != NULL)
515 *p = '_';
516 while ((p = strchr(sym, '+')) != NULL)
517 *p = '-';
518 *sym_out = sym;
519 return 0;
520 }
521
krb5_ccache_overwrite(context,ccs,cct,primary_principal)522 krb5_error_code krb5_ccache_overwrite(context, ccs, cct, primary_principal)
523 krb5_context context;
524 krb5_ccache ccs;
525 krb5_ccache cct;
526 krb5_principal primary_principal;
527 {
528 krb5_error_code retval=0;
529 krb5_principal temp_principal;
530 krb5_creds ** ccs_creds_arr = NULL;
531 int i=0;
532
533 if (ks_ccache_is_initialized(context, ccs)) {
534 if ((retval = krb5_get_nonexp_tkts(context, ccs, &ccs_creds_arr))){
535 return retval;
536 }
537 }
538
539 if (ks_ccache_is_initialized(context, cct)) {
540 if ((retval = krb5_cc_get_principal(context, cct, &temp_principal))){
541 return retval;
542 }
543 }else{
544 temp_principal = primary_principal;
545 }
546
547 if ((retval = krb5_cc_initialize(context, cct, temp_principal))){
548 return retval;
549 }
550
551 retval = krb5_store_all_creds(context, cct, ccs_creds_arr, NULL);
552
553 if (ccs_creds_arr){
554 while (ccs_creds_arr[i]){
555 krb5_free_creds(context, ccs_creds_arr[i]);
556 i++;
557 }
558 }
559
560 return retval;
561 }
562
krb5_store_some_creds(context,cc,creds_def,creds_other,prst,stored)563 krb5_error_code krb5_store_some_creds(context, cc, creds_def, creds_other, prst,
564 stored)
565 krb5_context context;
566 krb5_ccache cc;
567 krb5_creds **creds_def;
568 krb5_creds **creds_other;
569 krb5_principal prst;
570 krb5_boolean *stored;
571 {
572
573 int i = 0;
574 krb5_error_code retval = 0;
575 krb5_creds ** temp_creds= NULL;
576 krb5_boolean temp_stored = FALSE;
577
578
579 if ((creds_def == NULL) && (creds_other == NULL))
580 return 0;
581
582 if ((creds_def == NULL) && (creds_other != NULL))
583 temp_creds = creds_other;
584
585 if ((creds_def != NULL) && (creds_other == NULL))
586 temp_creds = creds_def;
587
588
589 if (temp_creds){
590 while(temp_creds[i]){
591 if (krb5_principal_compare(context,
592 temp_creds[i]->client,
593 prst)== TRUE) {
594
595 if ((retval = krb5_cc_store_cred(context,
596 cc,temp_creds[i]))){
597 return retval;
598 }
599 temp_stored = TRUE;
600 }
601
602 i++;
603 }
604 }
605 else { /* both arrays have elements in them */
606 return KRB5KRB_ERR_GENERIC;
607 }
608
609 *stored = temp_stored;
610 return 0;
611 }
612
krb5_ccache_filter(context,cc,prst)613 krb5_error_code krb5_ccache_filter (context, cc, prst)
614 krb5_context context;
615 krb5_ccache cc;
616 krb5_principal prst;
617 {
618
619 int i=0;
620 krb5_error_code retval=0;
621 krb5_principal temp_principal;
622 krb5_creds ** cc_creds_arr = NULL;
623 const char * cc_name;
624 krb5_boolean stored;
625
626 cc_name = krb5_cc_get_name(context, cc);
627
628 if (ks_ccache_is_initialized(context, cc)) {
629 if (auth_debug) {
630 fprintf(stderr,"putting cache %s through a filter for -z option\n", cc_name);
631 }
632
633 if ((retval = krb5_get_nonexp_tkts(context, cc, &cc_creds_arr))){
634 return retval;
635 }
636
637 if ((retval = krb5_cc_get_principal(context, cc, &temp_principal))){
638 return retval;
639 }
640
641 if ((retval = krb5_cc_initialize(context, cc, temp_principal))){
642 return retval;
643 }
644
645 if ((retval = krb5_store_some_creds(context, cc, cc_creds_arr,
646 NULL, prst, &stored))){
647 return retval;
648 }
649
650 if (cc_creds_arr){
651 while (cc_creds_arr[i]){
652 krb5_free_creds(context, cc_creds_arr[i]);
653 i++;
654 }
655 }
656 }
657 return 0;
658 }
659
krb5_find_princ_in_cred_list(context,creds_list,princ)660 krb5_boolean krb5_find_princ_in_cred_list (context, creds_list, princ)
661 krb5_context context;
662 krb5_creds **creds_list;
663 krb5_principal princ;
664 {
665
666 int i = 0;
667 krb5_boolean temp_stored = FALSE;
668
669 if (creds_list){
670 while(creds_list[i]){
671 if (krb5_principal_compare(context,
672 creds_list[i]->client,
673 princ)== TRUE){
674 temp_stored = TRUE;
675 break;
676 }
677
678 i++;
679 }
680 }
681
682 return temp_stored;
683 }
684
krb5_find_princ_in_cache(context,cc,princ,found)685 krb5_error_code krb5_find_princ_in_cache (context, cc, princ, found)
686 krb5_context context;
687 krb5_ccache cc;
688 krb5_principal princ;
689 krb5_boolean *found;
690 {
691 krb5_error_code retval;
692 krb5_creds ** creds_list = NULL;
693
694 if (ks_ccache_is_initialized(context, cc)) {
695 if ((retval = krb5_get_nonexp_tkts(context, cc, &creds_list))){
696 return retval;
697 }
698 }
699
700 *found = krb5_find_princ_in_cred_list(context, creds_list, princ);
701 return 0;
702 }
703
704 krb5_boolean
ks_ccache_name_is_initialized(krb5_context context,const char * cctag)705 ks_ccache_name_is_initialized(krb5_context context, const char *cctag)
706 {
707 krb5_boolean result;
708 krb5_ccache cc;
709
710 if (krb5_cc_resolve(context, cctag, &cc) != 0)
711 return FALSE;
712 result = ks_ccache_is_initialized(context, cc);
713 krb5_cc_close(context, cc);
714
715 return result;
716 }
717
718 krb5_boolean
ks_ccache_is_initialized(krb5_context context,krb5_ccache cc)719 ks_ccache_is_initialized(krb5_context context, krb5_ccache cc)
720 {
721 krb5_principal princ;
722 krb5_error_code retval;
723
724 if (cc == NULL)
725 return FALSE;
726
727 retval = krb5_cc_get_principal(context, cc, &princ);
728 if (retval == 0)
729 krb5_free_principal(context, princ);
730
731 return retval == 0;
732 }
733