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
31 #ifdef HAVE_UNISTD_H
32 #include <unistd.h>
33 #endif
34
35
36 /*******************************************************************
37 get_all_princ_from_file - retrieves all principal names
38 from file pointed to by fp.
39
40 *******************************************************************/
41 static void close_time (int, FILE *, int, FILE *);
42 static krb5_boolean find_str_in_list (char **, char *);
43
44 krb5_error_code
get_all_princ_from_file(FILE * fp,char *** plist)45 get_all_princ_from_file(FILE *fp, char ***plist)
46 {
47
48 krb5_error_code retval;
49 char * line, * fprinc, * lp, ** temp_list = NULL;
50 int count = 0, chunk_count = 1;
51
52 if (!(temp_list = (char **) malloc( CHUNK * sizeof(char *))))
53 return ENOMEM;
54
55 retval = get_line(fp, &line);
56 if (retval)
57 return retval;
58
59 while (line){
60 fprinc = get_first_token (line, &lp);
61
62 if (fprinc ){
63 temp_list[count] = xstrdup(fprinc);
64 count ++;
65 }
66
67 if(count == (chunk_count * CHUNK -1)){
68 chunk_count ++;
69 if (!(temp_list = (char **) realloc(temp_list,
70 chunk_count * CHUNK * sizeof(char *)))){
71 return ENOMEM;
72 }
73 }
74
75
76 free (line);
77 retval = get_line(fp, &line);
78 if (retval)
79 return retval;
80 }
81
82 temp_list[count] = NULL;
83
84 *plist = temp_list;
85 return 0;
86 }
87
88 /*************************************************************
89 list_union - combines list1 and list2 into combined_list.
90 the space for list1 and list2 is either freed
91 or used by combined_list.
92 **************************************************************/
93
94 krb5_error_code
list_union(char ** list1,char ** list2,char *** combined_list)95 list_union(char **list1, char **list2, char ***combined_list)
96 {
97
98 unsigned int c1 =0, c2 = 0, i=0, j=0;
99 char ** tlist;
100
101 if (! list1){
102 *combined_list = list2;
103 return 0;
104 }
105
106 if (! list2){
107 *combined_list = list1;
108 return 0;
109 }
110
111 while (list1[c1]) c1++;
112 while (list2[c2]) c2++;
113
114 if (!(tlist = (char **) calloc( c1 + c2 + 1, sizeof ( char *))))
115 return ENOMEM;
116
117 i = 0;
118 while(list1[i]) {
119 tlist[i] = list1[i];
120 i++;
121 }
122 j = 0;
123 while(list2[j]){
124 if(find_str_in_list(list1, list2[j])==FALSE){
125 tlist[i] = list2[j];
126 i++;
127 }
128 j++;
129 }
130
131 free (list1);
132 free (list2);
133
134 tlist[i]= NULL;
135
136 *combined_list = tlist;
137 return 0;
138 }
139
140 krb5_error_code
filter(FILE * fp,char * cmd,char ** k5users_list,char *** k5users_filt_list)141 filter(FILE *fp, char *cmd, char **k5users_list, char ***k5users_filt_list)
142 {
143
144 krb5_error_code retval =0;
145 krb5_boolean found = FALSE;
146 char * out_cmd = NULL;
147 unsigned int i=0, j=0, found_count = 0, k=0;
148 char ** temp_filt_list;
149
150 *k5users_filt_list = NULL;
151
152 if (k5users_list == NULL)
153 return 0;
154
155 while(k5users_list[i]){
156 free(out_cmd);
157 out_cmd = NULL;
158
159 retval= k5users_lookup(fp, k5users_list[i], cmd, &found, &out_cmd);
160 if (retval)
161 goto cleanup;
162
163 if (found == FALSE){
164 free (k5users_list[i]);
165 k5users_list[i] = NULL;
166 if (out_cmd) {
167 gb_err = out_cmd;
168 out_cmd = NULL;
169 }
170 } else
171 found_count ++;
172
173 i++;
174 }
175
176 temp_filt_list = xcalloc(found_count + 1, sizeof(*temp_filt_list));
177
178 for(j= 0, k=0; j < i; j++ ) {
179 if (k5users_list[j]){
180 temp_filt_list[k] = k5users_list[j];
181 k++;
182 }
183 }
184
185 temp_filt_list[k] = NULL;
186
187 free (k5users_list);
188
189 *k5users_filt_list = temp_filt_list;
190
191 cleanup:
192 free(out_cmd);
193 return retval;
194 }
195
196 krb5_error_code
get_authorized_princ_names(const char * luser,char * cmd,char *** princ_list)197 get_authorized_princ_names(const char *luser, char *cmd, char ***princ_list)
198 {
199
200 struct passwd *pwd;
201 int k5login_flag =0;
202 int k5users_flag =0;
203 FILE * login_fp = NULL , * users_fp = NULL;
204 char ** k5login_list = NULL, ** k5users_list = NULL;
205 char ** k5users_filt_list = NULL;
206 char ** combined_list = NULL;
207 struct stat tb;
208 krb5_error_code retval;
209
210 *princ_list = NULL;
211
212 /* no account => no access */
213
214 if ((pwd = getpwnam(luser)) == NULL)
215 return 0;
216
217 k5login_flag = stat(k5login_path, &tb);
218 k5users_flag = stat(k5users_path, &tb);
219
220 if (!k5login_flag){
221 if ((login_fp = fopen(k5login_path, "r")) == NULL)
222 return 0;
223 if ( fowner(login_fp, pwd->pw_uid) == FALSE){
224 close_time(1 /*k5users_flag*/, (FILE *) 0 /*users_fp*/,
225 k5login_flag,login_fp);
226 return 0;
227 }
228 }
229 if (!k5users_flag){
230 users_fp = fopen(k5users_path, "r");
231 if (users_fp == NULL) {
232 close_time(1, NULL, k5login_flag, login_fp);
233 return 0;
234 }
235
236 if ( fowner(users_fp, pwd->pw_uid) == FALSE){
237 close_time(k5users_flag,users_fp, k5login_flag,login_fp);
238 return 0;
239 }
240
241 retval = get_all_princ_from_file (users_fp, &k5users_list);
242 if(retval) {
243 close_time(k5users_flag,users_fp, k5login_flag,login_fp);
244 return retval;
245 }
246
247 rewind(users_fp);
248
249 retval = filter(users_fp,cmd, k5users_list, &k5users_filt_list);
250 if(retval) {
251 close_time(k5users_flag,users_fp, k5login_flag, login_fp);
252 return retval;
253 }
254 }
255
256 if (!k5login_flag){
257 retval = get_all_princ_from_file (login_fp, &k5login_list);
258 if(retval) {
259 close_time(k5users_flag,users_fp, k5login_flag,login_fp);
260 return retval;
261 }
262 }
263
264 close_time(k5users_flag,users_fp, k5login_flag, login_fp);
265
266 retval = list_union(k5login_list, k5users_filt_list, &combined_list);
267 if (retval){
268 return retval;
269 }
270 *princ_list = combined_list;
271 return 0;
272 }
273
274 static void
close_time(int k5users_flag,FILE * users_fp,int k5login_flag,FILE * login_fp)275 close_time(int k5users_flag, FILE *users_fp, int k5login_flag, FILE *login_fp)
276 {
277
278 if (!k5users_flag) fclose(users_fp);
279 if (!k5login_flag) fclose(login_fp);
280
281 }
282
283 static krb5_boolean
find_str_in_list(char ** list,char * elm)284 find_str_in_list(char **list, char *elm)
285 {
286
287 int i=0;
288 krb5_boolean found = FALSE;
289
290 if (!list) return found;
291
292 while (list[i] ){
293 if (!strcmp(list[i], elm)){
294 found = TRUE;
295 break;
296 }
297 i++;
298 }
299
300 return found;
301 }
302
303 /**********************************************************************
304 returns the principal that is closes to client (can be the the client
305 himself). plist contains
306 a principal list obtained from .k5login and .k5users file.
307 A principal is picked that has the best chance of getting in.
308
309 **********************************************************************/
310
311 krb5_error_code
get_closest_principal(krb5_context context,char ** plist,krb5_principal * client,krb5_boolean * found)312 get_closest_principal(krb5_context context, char **plist,
313 krb5_principal *client, krb5_boolean *found)
314 {
315 krb5_error_code retval =0;
316 krb5_principal temp_client, best_client = NULL;
317 int i = 0, j=0, cnelem, pnelem;
318 krb5_boolean got_one;
319
320 *found = FALSE;
321
322 if (! plist ) return 0;
323
324 cnelem = krb5_princ_size(context, *client);
325
326 while(plist[i]){
327
328 retval = krb5_parse_name(context, plist[i], &temp_client);
329 if (retval)
330 goto cleanup;
331
332 pnelem = krb5_princ_size(context, temp_client);
333
334 if ( cnelem > pnelem){
335 i++;
336 continue;
337 }
338
339 if (data_eq(*krb5_princ_realm(context, *client),
340 *krb5_princ_realm(context, temp_client))) {
341
342 got_one = TRUE;
343 for(j =0; j < cnelem; j ++){
344 krb5_data *p1 =
345 krb5_princ_component(context, *client, j);
346 krb5_data *p2 =
347 krb5_princ_component(context, temp_client, j);
348
349 if (!p1 || !p2 || !data_eq(*p1, *p2)) {
350 got_one = FALSE;
351 break;
352 }
353 }
354 if (got_one == TRUE){
355 if(best_client){
356 if(krb5_princ_size(context, best_client) >
357 krb5_princ_size(context, temp_client)){
358 krb5_free_principal(context, best_client);
359 best_client = temp_client;
360 }
361 }else
362 best_client = temp_client;
363 }
364 }
365 i++;
366 }
367
368 if (best_client) {
369 *found = TRUE;
370 *client = best_client;
371 best_client = NULL;
372 }
373
374 cleanup:
375 krb5_free_principal(context, best_client);
376 return retval;
377 }
378
379 /****************************************************************
380 find_either_ticket checks to see whether there is a ticket for the
381 end server or tgt, if neither is there the return FALSE,
382 *****************************************************************/
383
384 krb5_error_code
find_either_ticket(krb5_context context,krb5_ccache cc,krb5_principal client,krb5_principal end_server,krb5_boolean * found)385 find_either_ticket(krb5_context context, krb5_ccache cc, krb5_principal client,
386 krb5_principal end_server, krb5_boolean *found)
387 {
388
389 krb5_principal kdc_server;
390 krb5_error_code retval;
391 krb5_boolean temp_found = FALSE;
392
393 if (ks_ccache_is_initialized(context, cc)) {
394
395 retval = find_ticket(context, cc, client, end_server, &temp_found);
396 if (retval)
397 return retval;
398
399 if (temp_found == FALSE){
400 retval = ksu_tgtname(context,
401 krb5_princ_realm(context, client),
402 krb5_princ_realm(context, client),
403 &kdc_server);
404 if (retval)
405 return retval;
406
407 retval = find_ticket(context, cc,client, kdc_server, &temp_found);
408 if(retval)
409 return retval;
410 }
411 else if (auth_debug)
412 printf("find_either_ticket: found end server ticket\n");
413 }
414
415 *found = temp_found;
416
417 return 0;
418 }
419
420 krb5_error_code
find_ticket(krb5_context context,krb5_ccache cc,krb5_principal client,krb5_principal server,krb5_boolean * found)421 find_ticket(krb5_context context, krb5_ccache cc, krb5_principal client,
422 krb5_principal server, krb5_boolean *found)
423 {
424
425 krb5_creds tgt, tgtq;
426 krb5_error_code retval;
427
428 *found = FALSE;
429
430 memset(&tgtq, 0, sizeof(tgtq));
431 memset(&tgt, 0, sizeof(tgt));
432
433 retval= krb5_copy_principal(context, client, &tgtq.client);
434 if (retval)
435 return retval;
436
437 retval= krb5_copy_principal(context, server, &tgtq.server);
438 if (retval)
439 return retval ;
440
441 retval = krb5_cc_retrieve_cred(context, cc, KRB5_TC_MATCH_SRV_NAMEONLY | KRB5_TC_SUPPORTED_KTYPES,
442 &tgtq, &tgt);
443
444 if (! retval) retval = krb5_check_exp(context, tgt.times);
445
446 if (retval){
447 if ((retval != KRB5_CC_NOTFOUND) &&
448 (retval != KRB5KRB_AP_ERR_TKT_EXPIRED)){
449 return retval ;
450 }
451 } else{
452 *found = TRUE;
453 return 0;
454 }
455
456 free(tgtq.server);
457 free(tgtq.client);
458
459 return 0;
460 }
461
462 krb5_error_code
find_princ_in_list(krb5_context context,krb5_principal princ,char ** plist,krb5_boolean * found)463 find_princ_in_list(krb5_context context, krb5_principal princ, char **plist,
464 krb5_boolean *found)
465 {
466
467 int i=0;
468 char * princname;
469 krb5_error_code retval;
470
471 *found = FALSE;
472
473 if (!plist) return 0;
474
475 retval = krb5_unparse_name(context, princ, &princname);
476 if (retval)
477 return retval;
478
479 while (plist[i] ){
480 if (!strcmp(plist[i], princname)){
481 *found = TRUE;
482 break;
483 }
484 i++;
485 }
486
487 free(princname);
488 return 0;
489
490 }
491
492 typedef struct princ_info {
493 krb5_principal p;
494 krb5_boolean found;
495 }princ_info;
496
497 /**********************************************************************
498 get_best_princ_for_target -
499
500 sets the client name, path_out gets set, if authorization is not possible
501 path_out gets set to ...
502
503 ***********************************************************************/
504
505 krb5_error_code
get_best_princ_for_target(krb5_context context,uid_t source_uid,uid_t target_uid,char * source_user,char * target_user,krb5_ccache cc_source,krb5_get_init_creds_opt * options,char * cmd,char * hostname,krb5_principal * client,int * path_out)506 get_best_princ_for_target(krb5_context context, uid_t source_uid,
507 uid_t target_uid, char *source_user,
508 char *target_user, krb5_ccache cc_source,
509 krb5_get_init_creds_opt *options, char *cmd,
510 char *hostname, krb5_principal *client,
511 int *path_out)
512 {
513
514 princ_info princ_trials[10];
515 krb5_principal cc_def_princ = NULL, temp_client = NULL;
516 krb5_principal target_client = NULL, source_client = NULL;
517 krb5_principal end_server = NULL;
518 krb5_error_code retval;
519 char ** aplist =NULL;
520 krb5_boolean found = FALSE;
521 struct stat tb;
522 int count =0;
523 int i;
524
525 *path_out = 0;
526
527 /* -n option was specified client is set we are done */
528 if (*client != NULL)
529 return 0;
530
531 if (ks_ccache_is_initialized(context, cc_source)) {
532 retval = krb5_cc_get_principal(context, cc_source, &cc_def_princ);
533 if (retval)
534 goto cleanup;
535 }
536
537 retval=krb5_parse_name(context, target_user, &target_client);
538 if (retval)
539 goto cleanup;
540
541 retval=krb5_parse_name(context, source_user, &source_client);
542 if (retval)
543 goto cleanup;
544
545 if (source_uid == 0) {
546 if (target_uid != 0) {
547 /* This will be used to restrict the cache copy. */
548 *client = target_client;
549 target_client = NULL;
550 } else if (cc_def_princ != NULL) {
551 *client = cc_def_princ;
552 cc_def_princ = NULL;
553 } else {
554 *client = target_client;
555 target_client = NULL;
556 }
557 if (auth_debug)
558 printf(" GET_best_princ_for_target: via source_uid == 0\n");
559 goto cleanup;
560 }
561
562 /* from here on, the code is for source_uid != 0 */
563
564 if (source_uid && (source_uid == target_uid)){
565 if (cc_def_princ != NULL) {
566 *client = cc_def_princ;
567 cc_def_princ = NULL;
568 } else {
569 *client = target_client;
570 target_client = NULL;
571 }
572 if (auth_debug)
573 printf("GET_best_princ_for_target: via source_uid == target_uid\n");
574 goto cleanup;
575 }
576
577 /* Become root, then target for looking at .k5login.*/
578 if (krb5_seteuid(0) || krb5_seteuid(target_uid) ) {
579 retval = errno;
580 goto cleanup;
581 }
582
583 /* if .k5users and .k5login do not exist */
584 if (stat(k5login_path, &tb) && stat(k5users_path, &tb) ){
585 *client = target_client;
586 target_client = NULL;
587
588 if (cmd)
589 *path_out = NOT_AUTHORIZED;
590
591 if (auth_debug)
592 printf(" GET_best_princ_for_target: via no auth files path\n");
593
594 goto cleanup;
595 }else{
596 retval = get_authorized_princ_names(target_user, cmd, &aplist);
597 if (retval)
598 goto cleanup;
599
600 /* .k5users or .k5login exist, but no authorization */
601 if ((!aplist) || (!aplist[0])) {
602 *path_out = NOT_AUTHORIZED;
603 if (auth_debug)
604 printf("GET_best_princ_for_target: via empty auth files path\n");
605 goto cleanup;
606 }
607 }
608
609 retval = krb5_sname_to_principal(context, hostname, NULL,
610 KRB5_NT_SRV_HST, &end_server);
611 if (retval)
612 goto cleanup;
613
614 /* first see if default principal of the source cache
615 * can get us in, then the target_user@realm, then the
616 * source_user@realm. If all of them fail, try any
617 * other ticket in the cache. */
618
619 if (cc_def_princ)
620 princ_trials[count ++].p = cc_def_princ;
621 else
622 princ_trials[count ++].p = NULL;
623
624 princ_trials[count ++].p = target_client;
625 princ_trials[count ++].p = source_client;
626
627 for (i= 0; i < count; i ++)
628 princ_trials[i].found = FALSE;
629
630 for (i= 0; i < count; i ++){
631 if(princ_trials[i].p) {
632 retval= find_princ_in_list(context, princ_trials[i].p, aplist,
633 &found);
634 if (retval)
635 goto cleanup;
636
637 if (found == TRUE){
638 princ_trials[i].found = TRUE;
639
640 retval = find_either_ticket (context, cc_source,
641 princ_trials[i].p,
642 end_server, &found);
643 if (retval)
644 goto cleanup;
645 if (found == TRUE){
646 retval = krb5_copy_principal(context, princ_trials[i].p,
647 client);
648 if (auth_debug)
649 printf("GET_best_princ_for_target: via ticket file, choice #%d\n", i);
650 goto cleanup;
651 }
652 }
653 }
654 }
655
656 /* out of preferred principals, see if there is any ticket that will
657 get us in */
658
659 i=0;
660 while (aplist[i]){
661 retval = krb5_parse_name(context, aplist[i], &temp_client);
662 if (retval)
663 goto cleanup;
664
665 retval = find_either_ticket (context, cc_source, temp_client,
666 end_server, &found);
667 if (retval)
668 goto cleanup;
669
670 if (found == TRUE){
671 if (auth_debug)
672 printf("GET_best_princ_for_target: via ticket file, choice: any ok ticket \n" );
673 *client = temp_client;
674 temp_client = NULL;
675 goto cleanup;
676 }
677
678 krb5_free_principal(context, temp_client);
679 temp_client = NULL;
680
681 i++;
682 }
683
684 /* no tickets qualified, select a principal, that may be used
685 for password promting */
686
687
688 for (i=0; i < count; i ++){
689 if (princ_trials[i].found == TRUE){
690 retval = krb5_copy_principal(context, princ_trials[i].p, client);
691
692 if (auth_debug)
693 printf("GET_best_princ_for_target: via prompt passwd list choice #%d \n",i);
694 goto cleanup;
695 }
696 }
697
698 #ifdef PRINC_LOOK_AHEAD
699 for (i=0; i < count; i ++){
700 if (princ_trials[i].p){
701 retval=krb5_copy_principal(context, princ_trials[i].p,
702 &temp_client);
703 if(retval)
704 goto cleanup;
705
706 /* get the client name that is the closest
707 to the three princ in trials */
708
709 retval=get_closest_principal(context, aplist, &temp_client,
710 &found);
711 if(retval)
712 goto cleanup;
713
714 if (found == TRUE){
715 *client = temp_client;
716 temp_client = NULL;
717 if (auth_debug)
718 printf("GET_best_princ_for_target: via prompt passwd list choice: approximation of princ in trials # %d \n",i);
719 goto cleanup;
720 }
721 }
722 }
723
724 #endif /* PRINC_LOOK_AHEAD */
725
726
727 if(auth_debug)
728 printf( "GET_best_princ_for_target: out of luck, can't get appropriate default principal\n");
729
730 *path_out = NOT_AUTHORIZED;
731 retval = 0;
732
733 cleanup:
734 krb5_free_principal(context, cc_def_princ);
735 krb5_free_principal(context, target_client);
736 krb5_free_principal(context, source_client);
737 krb5_free_principal(context, temp_client);
738 krb5_free_principal(context, end_server);
739 return retval;
740 }
741