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, ¤ttime))){
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