Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/krb5/src/clients/ksu/heuristic.c
34889 views
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, [email protected]
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
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
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
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
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
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
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
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
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
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
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
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
742