xref: /freebsd/crypto/krb5/src/clients/ksu/ccache.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
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, &currenttime))){
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