xref: /freebsd/crypto/krb5/src/clients/ksu/ccache.c (revision f1c4c3daccbaf3820f0e2224de53df12fc952fcc)
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 static void
free_creds_list(krb5_context context,krb5_creds ** list)44 free_creds_list(krb5_context context, krb5_creds **list)
45 {
46     size_t i;
47 
48     if (list == NULL)
49         return;
50     for (i = 0; list[i]; i++)
51         krb5_free_creds(context, list[i]);
52     free(list);
53 }
54 
55 void show_credential(krb5_context, krb5_creds *, krb5_ccache);
56 
57 /* modifies only the cc_other, the algorithm may look a bit funny,
58    but I had to do it this way, since remove function did not come
59    with k5 beta 3 release.
60 */
61 
62 krb5_error_code
krb5_ccache_copy(krb5_context context,krb5_ccache cc_def,krb5_principal target_principal,krb5_ccache cc_target,krb5_boolean restrict_creds,krb5_principal primary_principal,krb5_boolean * stored)63 krb5_ccache_copy(krb5_context context, krb5_ccache cc_def,
64                  krb5_principal target_principal, krb5_ccache cc_target,
65                  krb5_boolean restrict_creds, krb5_principal primary_principal,
66                  krb5_boolean *stored)
67 {
68     krb5_error_code retval=0;
69     krb5_creds ** cc_def_creds_arr = NULL;
70     krb5_creds ** cc_other_creds_arr = NULL;
71 
72     if (ks_ccache_is_initialized(context, cc_def)) {
73         retval = krb5_get_nonexp_tkts(context, cc_def, &cc_def_creds_arr);
74         if (retval)
75             goto cleanup;
76     }
77 
78     retval = krb5_cc_initialize(context, cc_target, target_principal);
79     if (retval)
80         goto cleanup;
81 
82     if (restrict_creds) {
83         retval = krb5_store_some_creds(context, cc_target, cc_def_creds_arr,
84                                        cc_other_creds_arr, primary_principal,
85                                        stored);
86     } else {
87         *stored = krb5_find_princ_in_cred_list(context, cc_def_creds_arr,
88                                                primary_principal);
89         retval = krb5_store_all_creds(context, cc_target, cc_def_creds_arr,
90                                       cc_other_creds_arr);
91     }
92 
93 cleanup:
94     free_creds_list(context, cc_def_creds_arr);
95     free_creds_list(context, cc_other_creds_arr);
96     return retval;
97 }
98 
99 
100 krb5_error_code
krb5_store_all_creds(krb5_context context,krb5_ccache cc,krb5_creds ** creds_def,krb5_creds ** creds_other)101 krb5_store_all_creds(krb5_context context, krb5_ccache cc,
102                      krb5_creds **creds_def, krb5_creds **creds_other)
103 {
104 
105     int i = 0;
106     krb5_error_code retval = 0;
107     krb5_creds ** temp_creds= NULL;
108 
109 
110     if ((creds_def == NULL) && (creds_other == NULL))
111         return 0;
112 
113     if ((creds_def == NULL) && (creds_other != NULL))
114         temp_creds = creds_other;
115 
116     if ((creds_def != NULL) && (creds_other == NULL))
117         temp_creds = creds_def;
118 
119 
120     if (temp_creds){
121         while(temp_creds[i]){
122             if ((retval= krb5_cc_store_cred(context, cc,
123                                             temp_creds[i]))){
124                 return retval;
125             }
126             i++;
127         }
128     }
129     else { /* both arrays have elements in them */
130 
131         return  KRB5KRB_ERR_GENERIC;
132 
133 /************   while(creds_other[i]){
134                         cmp = FALSE;
135                         j = 0;
136                         while(creds_def[j]){
137                            cmp = compare_creds(creds_other[i],creds_def[j]);
138 
139                            if( cmp == TRUE) break;
140 
141                            j++;
142                         }
143                         if (cmp == FALSE){
144                                 if (retval= krb5_cc_store_cred(context, cc,
145                                                          creds_other[i])){
146                                                 return retval;
147                                 }
148                         }
149                         i ++;
150                 }
151 
152                 i=0;
153                 while(creds_def[i]){
154                         if (retval= krb5_cc_store_cred(context, cc,
155                                                        creds_def[i])){
156                                 return retval;
157                         }
158                         i++;
159                 }
160 
161 **************/
162     }
163     return 0;
164 }
165 
166 krb5_boolean
compare_creds(krb5_context context,krb5_creds * cred1,krb5_creds * cred2)167 compare_creds(krb5_context context, krb5_creds *cred1, krb5_creds *cred2)
168 {
169     krb5_boolean retval;
170 
171     retval = krb5_principal_compare (context, cred1->client, cred2->client);
172 
173     if (retval == TRUE)
174         retval = krb5_principal_compare (context, cred1->server,                                                         cred2->server);
175 
176     return retval;
177 }
178 
179 krb5_error_code
krb5_get_nonexp_tkts(krb5_context context,krb5_ccache cc,krb5_creds *** creds_array)180 krb5_get_nonexp_tkts(krb5_context context, krb5_ccache cc,
181                      krb5_creds ***creds_array)
182 {
183 
184     krb5_creds creds, temp_tktq, temp_tkt;
185     krb5_creds **temp_creds = NULL;
186     krb5_error_code retval=0;
187     krb5_cc_cursor cur;
188     int count = 0;
189     int chunk_count = 1;
190 
191     temp_creds = xcalloc(CHUNK, sizeof(*temp_creds));
192     memset(&temp_tktq, 0, sizeof(temp_tktq));
193     memset(&temp_tkt, 0, sizeof(temp_tkt));
194     memset(&creds, 0, sizeof(creds));
195 
196     /* initialize the cursor */
197     retval = krb5_cc_start_seq_get(context, cc, &cur);
198     if (retval)
199         goto cleanup;
200 
201     while (!(retval = krb5_cc_next_cred(context, cc, &cur, &creds))){
202 
203         if (!krb5_is_config_principal(context, creds.server) &&
204             (retval = krb5_check_exp(context, creds.times))){
205             krb5_free_cred_contents(context, &creds);
206             if (retval != KRB5KRB_AP_ERR_TKT_EXPIRED){
207                 goto cleanup;
208             }
209             if (auth_debug){
210                 fprintf(stderr,"krb5_ccache_copy: CREDS EXPIRED:\n");
211                 fputs("  Valid starting         Expires         Service principal\n",stdout);
212                 show_credential(context, &creds, cc);
213                 fprintf(stderr,"\n");
214             }
215         }
216         else {   /* these credentials didn't expire */
217             retval = krb5_copy_creds(context, &creds, &temp_creds[count]);
218             krb5_free_cred_contents(context, &creds);
219             temp_creds[count+1] = NULL;
220             if (retval)
221                 goto cleanup;
222             count ++;
223 
224             if (count == (chunk_count * CHUNK -1)){
225                 chunk_count ++;
226 
227                 temp_creds = xrealloc(temp_creds,
228                                       chunk_count * CHUNK *
229                                       sizeof(*temp_creds));
230             }
231         }
232 
233     }
234 
235     temp_creds[count] = NULL;
236     *creds_array   = temp_creds;
237     temp_creds = NULL;
238 
239     if (retval == KRB5_CC_END) {
240         retval = krb5_cc_end_seq_get(context, cc, &cur);
241     }
242 
243 cleanup:
244     free_creds_list(context, temp_creds);
245     return retval;
246 }
247 
248 krb5_error_code
krb5_check_exp(krb5_context context,krb5_ticket_times tkt_time)249 krb5_check_exp(krb5_context context, krb5_ticket_times tkt_time)
250 {
251     krb5_error_code retval =0;
252     krb5_timestamp currenttime;
253 
254     if ((retval = krb5_timeofday (context, &currenttime))){
255         return retval;
256     }
257     if (auth_debug){
258         fprintf(stderr,"krb5_check_exp: the krb5_clockskew is %d \n",
259                 context->clockskew);
260 
261         fprintf(stderr,"krb5_check_exp: currenttime - endtime %d \n",
262                 ts_delta(currenttime, tkt_time.endtime));
263 
264     }
265 
266     if (ts_after(currenttime, ts_incr(tkt_time.endtime, context->clockskew))) {
267         retval = KRB5KRB_AP_ERR_TKT_EXPIRED ;
268         return retval;
269     }
270 
271     return 0;
272 }
273 
274 char *
flags_string(krb5_creds * cred)275 flags_string(krb5_creds *cred)
276 {
277     static char buf[32];
278     int i = 0;
279 
280     if (cred->ticket_flags & TKT_FLG_FORWARDABLE)
281         buf[i++] = 'F';
282     if (cred->ticket_flags & TKT_FLG_FORWARDED)
283         buf[i++] = 'f';
284     if (cred->ticket_flags & TKT_FLG_PROXIABLE)
285         buf[i++] = 'P';
286     if (cred->ticket_flags & TKT_FLG_PROXY)
287         buf[i++] = 'p';
288     if (cred->ticket_flags & TKT_FLG_MAY_POSTDATE)
289         buf[i++] = 'D';
290     if (cred->ticket_flags & TKT_FLG_POSTDATED)
291         buf[i++] = 'd';
292     if (cred->ticket_flags & TKT_FLG_INVALID)
293         buf[i++] = 'i';
294     if (cred->ticket_flags & TKT_FLG_RENEWABLE)
295         buf[i++] = 'R';
296     if (cred->ticket_flags & TKT_FLG_INITIAL)
297         buf[i++] = 'I';
298     if (cred->ticket_flags & TKT_FLG_HW_AUTH)
299         buf[i++] = 'H';
300     if (cred->ticket_flags & TKT_FLG_PRE_AUTH)
301         buf[i++] = 'A';
302     buf[i] = '\0';
303     return(buf);
304 }
305 
306 void
printtime(krb5_timestamp ts)307 printtime(krb5_timestamp ts)
308 {
309     char fmtbuf[18], fill = ' ';
310 
311     if (!krb5_timestamp_to_sfstring(ts, fmtbuf, sizeof(fmtbuf), &fill))
312         printf("%s", fmtbuf);
313 }
314 
315 void
show_credential(krb5_context context,krb5_creds * cred,krb5_ccache cc)316 show_credential(krb5_context context, krb5_creds *cred, krb5_ccache cc)
317 {
318     krb5_error_code retval;
319     char *name = NULL, *sname = NULL, *defname = NULL, *flags;
320     int first = 1;
321     krb5_principal princ = NULL;
322     int show_flags =1;
323 
324     retval = krb5_unparse_name(context, cred->client, &name);
325     if (retval) {
326         com_err(prog_name, retval, _("while unparsing client name"));
327         goto cleanup;
328     }
329     retval = krb5_unparse_name(context, cred->server, &sname);
330     if (retval) {
331         com_err(prog_name, retval, _("while unparsing server name"));
332         goto cleanup;
333     }
334 
335     if ((retval = krb5_cc_get_principal(context, cc, &princ))) {
336         com_err(prog_name, retval, _("while retrieving principal name"));
337         goto cleanup;
338     }
339     if ((retval = krb5_unparse_name(context, princ, &defname))) {
340         com_err(prog_name, retval, _("while unparsing principal name"));
341         goto cleanup;
342     }
343 
344     if (!cred->times.starttime)
345         cred->times.starttime = cred->times.authtime;
346 
347     printtime(cred->times.starttime);
348     putchar(' '); putchar(' ');
349     printtime(cred->times.endtime);
350     putchar(' '); putchar(' ');
351 
352     printf("%s\n", sname);
353 
354     if (strcmp(name, defname)) {
355         printf(_("\tfor client %s"), name);
356         first = 0;
357     }
358 
359     if (cred->times.renew_till) {
360         if (first)
361             fputs("\t",stdout);
362         else
363             fputs(", ",stdout);
364         fputs(_("renew until "), stdout);
365         printtime(cred->times.renew_till);
366     }
367     if (show_flags) {
368         flags = flags_string(cred);
369         if (flags && *flags) {
370             if (first)
371                 fputs("\t",stdout);
372             else
373                 fputs(", ",stdout);
374             printf(_("Flags: %s"), flags);
375             first = 0;
376         }
377     }
378     putchar('\n');
379 
380 cleanup:
381     free(name);
382     free(sname);
383     free(defname);
384     krb5_free_principal(context, princ);
385 }
386 
387 /* Create a random string suitable for a filename extension. */
388 krb5_error_code
gen_sym(krb5_context context,char ** sym_out)389 gen_sym(krb5_context context, char **sym_out)
390 {
391     krb5_error_code retval;
392     char bytes[6], *p, *sym;
393     krb5_data data = make_data(bytes, sizeof(bytes));
394 
395     *sym_out = NULL;
396     retval = krb5_c_random_make_octets(context, &data);
397     if (retval)
398         return retval;
399     sym = k5_base64_encode(data.data, data.length);
400     if (sym == NULL)
401         return ENOMEM;
402     /* Tweak the output alphabet just a bit. */
403     while ((p = strchr(sym, '/')) != NULL)
404         *p = '_';
405     while ((p = strchr(sym, '+')) != NULL)
406         *p = '-';
407     *sym_out = sym;
408     return 0;
409 }
410 
411 krb5_error_code
krb5_ccache_overwrite(krb5_context context,krb5_ccache ccs,krb5_ccache cct,krb5_principal primary_principal)412 krb5_ccache_overwrite(krb5_context context, krb5_ccache ccs, krb5_ccache cct,
413                       krb5_principal primary_principal)
414 {
415     krb5_error_code retval=0;
416     krb5_principal defprinc = NULL, princ;
417     krb5_creds ** ccs_creds_arr = NULL;
418 
419     if (ks_ccache_is_initialized(context, ccs)) {
420         retval = krb5_get_nonexp_tkts(context,  ccs, &ccs_creds_arr);
421         if (retval)
422             goto cleanup;
423     }
424 
425     retval = krb5_cc_get_principal(context, cct, &defprinc);
426     princ = (retval == 0) ? defprinc : primary_principal;
427     retval = krb5_cc_initialize(context, cct, princ);
428     if (retval)
429         goto cleanup;
430 
431     retval = krb5_store_all_creds(context, cct, ccs_creds_arr, NULL);
432 
433 cleanup:
434     free_creds_list(context, ccs_creds_arr);
435     krb5_free_principal(context, defprinc);
436     return retval;
437 }
438 
439 krb5_error_code
krb5_store_some_creds(krb5_context context,krb5_ccache cc,krb5_creds ** creds_def,krb5_creds ** creds_other,krb5_principal prst,krb5_boolean * stored)440 krb5_store_some_creds(krb5_context context, krb5_ccache cc,
441                       krb5_creds **creds_def, krb5_creds **creds_other,
442                       krb5_principal prst, krb5_boolean *stored)
443 {
444 
445     int i = 0;
446     krb5_error_code retval = 0;
447     krb5_creds ** temp_creds= NULL;
448     krb5_boolean temp_stored = FALSE;
449 
450 
451     if ((creds_def == NULL) && (creds_other == NULL))
452         return 0;
453 
454     if ((creds_def == NULL) && (creds_other != NULL))
455         temp_creds = creds_other;
456 
457     if ((creds_def != NULL) && (creds_other == NULL))
458         temp_creds = creds_def;
459 
460 
461     if (temp_creds){
462         while(temp_creds[i]){
463             if (krb5_principal_compare(context,
464                                        temp_creds[i]->client,
465                                        prst)== TRUE) {
466 
467                 if ((retval = krb5_cc_store_cred(context,
468                                                  cc,temp_creds[i]))){
469                     return retval;
470                 }
471                 temp_stored = TRUE;
472             }
473 
474             i++;
475         }
476     }
477     else { /* both arrays have elements in them */
478         return KRB5KRB_ERR_GENERIC;
479     }
480 
481     *stored = temp_stored;
482     return 0;
483 }
484 
485 krb5_error_code
krb5_ccache_filter(krb5_context context,krb5_ccache cc,krb5_principal prst)486 krb5_ccache_filter(krb5_context context, krb5_ccache cc, krb5_principal prst)
487 {
488 
489     krb5_error_code retval=0;
490     krb5_principal temp_principal = NULL;
491     krb5_creds ** cc_creds_arr = NULL;
492     const char * cc_name;
493     krb5_boolean stored;
494 
495     if (!ks_ccache_is_initialized(context, cc))
496         return 0;
497 
498     if (auth_debug) {
499         cc_name = krb5_cc_get_name(context, cc);
500         fprintf(stderr, "putting cache %s through a filter for -z option\n",
501                 cc_name);
502     }
503 
504     retval = krb5_get_nonexp_tkts(context, cc, &cc_creds_arr);
505     if (retval)
506         goto cleanup;
507 
508     retval = krb5_cc_get_principal(context, cc, &temp_principal);
509     if (retval)
510         goto cleanup;
511 
512     retval = krb5_cc_initialize(context, cc, temp_principal);
513     if (retval)
514         goto cleanup;
515 
516     retval = krb5_store_some_creds(context, cc, cc_creds_arr, NULL, prst,
517                                    &stored);
518 
519 cleanup:
520     free_creds_list(context, cc_creds_arr);
521     krb5_free_principal(context, temp_principal);
522     return retval;
523 }
524 
525 krb5_boolean
krb5_find_princ_in_cred_list(krb5_context context,krb5_creds ** creds_list,krb5_principal princ)526 krb5_find_princ_in_cred_list(krb5_context context, krb5_creds **creds_list,
527                              krb5_principal princ)
528 {
529 
530     int i = 0;
531     krb5_boolean temp_stored = FALSE;
532 
533     if (creds_list){
534         while(creds_list[i]){
535             if (krb5_principal_compare(context,
536                                        creds_list[i]->client,
537                                        princ)== TRUE){
538                 temp_stored = TRUE;
539                 break;
540             }
541 
542             i++;
543         }
544     }
545 
546     return temp_stored;
547 }
548 
549 krb5_error_code
krb5_find_princ_in_cache(krb5_context context,krb5_ccache cc,krb5_principal princ,krb5_boolean * found)550 krb5_find_princ_in_cache(krb5_context context, krb5_ccache cc,
551                          krb5_principal princ, krb5_boolean *found)
552 {
553     krb5_error_code retval = 0;
554     krb5_creds ** creds_list = NULL;
555 
556     if (ks_ccache_is_initialized(context, cc)) {
557         retval = krb5_get_nonexp_tkts(context, cc, &creds_list);
558         if (retval)
559             goto cleanup;
560     }
561 
562     *found = krb5_find_princ_in_cred_list(context, creds_list, princ);
563 
564 cleanup:
565     free_creds_list(context, creds_list);
566     return retval;
567 }
568 
569 krb5_boolean
ks_ccache_name_is_initialized(krb5_context context,const char * cctag)570 ks_ccache_name_is_initialized(krb5_context context, const char *cctag)
571 {
572     krb5_boolean result;
573     krb5_ccache cc;
574 
575     if (krb5_cc_resolve(context, cctag, &cc) != 0)
576         return FALSE;
577     result = ks_ccache_is_initialized(context, cc);
578     krb5_cc_close(context, cc);
579 
580     return result;
581 }
582 
583 krb5_boolean
ks_ccache_is_initialized(krb5_context context,krb5_ccache cc)584 ks_ccache_is_initialized(krb5_context context, krb5_ccache cc)
585 {
586     krb5_principal princ;
587     krb5_error_code retval;
588 
589     if (cc == NULL)
590         return FALSE;
591 
592     retval = krb5_cc_get_principal(context, cc, &princ);
593     if (retval == 0)
594         krb5_free_principal(context, princ);
595 
596     return retval == 0;
597 }
598