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