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 static void
free_fcmd_list(char ** list)32 free_fcmd_list(char **list)
33 {
34 size_t i;
35
36 if (list == NULL)
37 return;
38 for (i = 0; i < MAX_CMD && list[i] != NULL; i++)
39 free(list[i]);
40 free(list);
41 }
42
43 krb5_boolean
fowner(FILE * fp,uid_t uid)44 fowner(FILE *fp, uid_t uid)
45 {
46 struct stat sbuf;
47
48 /*
49 * For security reasons, file must be owned either by
50 * the user himself, or by root. Otherwise, don't grant access.
51 */
52 if (fstat(fileno(fp), &sbuf)) {
53 return(FALSE);
54 }
55
56 if ((sbuf.st_uid != uid) && sbuf.st_uid) {
57 return(FALSE);
58 }
59
60 return(TRUE);
61 }
62
63 /*
64 * Given a Kerberos principal "principal", and a local username "luser",
65 * determine whether user is authorized to login according to the authorization
66 * files ~luser/.k5login" and ~luser/.k5users. Set *ok to TRUE if authorized,
67 * FALSE if not authorized. Return 0 if the authorization check succeeded
68 * (regardless of its result), non-zero if it encountered an error.
69 */
70
71 krb5_error_code
krb5_authorization(krb5_context context,krb5_principal principal,const char * luser,char * cmd,krb5_boolean * ok,char ** out_fcmd)72 krb5_authorization(krb5_context context, krb5_principal principal,
73 const char *luser, char *cmd, krb5_boolean *ok,
74 char **out_fcmd)
75 {
76 struct passwd *pwd;
77 char *princname = NULL;
78 int k5login_flag =0;
79 int k5users_flag =0;
80 krb5_boolean retbool =FALSE;
81 FILE * login_fp = 0, * users_fp = 0;
82 krb5_error_code retval = 0;
83 struct stat st_temp;
84
85 *ok =FALSE;
86
87 /* no account => no access */
88 if ((pwd = getpwnam(luser)) == NULL)
89 goto cleanup;
90
91 retval = krb5_unparse_name(context, principal, &princname);
92 if (retval)
93 return retval;
94
95 #ifdef DEBUG
96 printf("principal to be authorized %s\n", princname);
97 printf("login file: %s\n", k5login_path);
98 printf("users file: %s\n", k5users_path);
99 #endif
100
101 k5login_flag = stat(k5login_path, &st_temp);
102 k5users_flag = stat(k5users_path, &st_temp);
103
104 /* k5login and k5users must be owned by target user or root */
105 if (!k5login_flag){
106 login_fp = fopen(k5login_path, "r");
107 if (login_fp == NULL)
108 goto cleanup;
109 if (fowner(login_fp, pwd->pw_uid) == FALSE)
110 goto cleanup;
111 }
112
113 if (!k5users_flag){
114 users_fp = fopen(k5users_path, "r");
115 if (users_fp == NULL)
116 goto cleanup;
117 if (fowner(users_fp, pwd->pw_uid) == FALSE)
118 goto cleanup;
119 }
120
121 if (auth_debug){
122 fprintf(stderr,
123 "In krb5_authorization: if auth files exist -> can access\n");
124 }
125
126 /* if either file exists,
127 first see if the principal is in the login in file,
128 if it's not there check the k5users file */
129
130 if (!k5login_flag){
131 if (auth_debug)
132 fprintf(stderr,
133 "In krb5_authorization: principal to be authorized %s\n",
134 princname);
135
136 retval = k5login_lookup(login_fp, princname, &retbool);
137 if (retval)
138 goto cleanup;
139 if (retbool) {
140 if (cmd)
141 *out_fcmd = xstrdup(cmd);
142 }
143 }
144
145 if ((!k5users_flag) && (retbool == FALSE) ){
146 retval = k5users_lookup (users_fp, princname,
147 cmd, &retbool, out_fcmd);
148 if (retval)
149 goto cleanup;
150 }
151
152 if (k5login_flag && k5users_flag){
153
154 char * kuser = (char *) xcalloc (strlen(princname), sizeof(char));
155 if (!(krb5_aname_to_localname(context, principal,
156 strlen(princname), kuser))
157 && (strcmp(kuser, luser) == 0)) {
158 retbool = TRUE;
159 }
160
161 free(kuser);
162 }
163
164 *ok =retbool;
165
166 cleanup:
167 if (users_fp != NULL)
168 fclose(users_fp);
169 if (login_fp != NULL)
170 fclose(login_fp);
171 free(princname);
172 return retval;
173 }
174
175 /***********************************************************
176 k5login_lookup looks for princname in file fp. Spaces
177 before the princaname (in the file ) are not ignored
178 spaces after the princname are ignored. If there are
179 any tokens after the principal name FALSE is returned.
180
181 ***********************************************************/
182
183 krb5_error_code
k5login_lookup(FILE * fp,char * princname,krb5_boolean * found)184 k5login_lookup(FILE *fp, char *princname, krb5_boolean *found)
185 {
186
187 krb5_error_code retval;
188 char * line;
189 char * fprinc;
190 char * lp;
191 krb5_boolean loc_found = FALSE;
192
193 retval = get_line(fp, &line);
194 if (retval)
195 return retval;
196
197 while (line){
198 fprinc = get_first_token (line, &lp);
199
200 if (fprinc && (!strcmp(princname, fprinc))){
201 if( get_next_token (&lp) ){
202 free (line);
203 break; /* nothing should follow princname*/
204 }
205 else{
206 loc_found = TRUE;
207 free (line);
208 break;
209 }
210 }
211
212 free (line);
213
214 retval = get_line(fp, &line);
215 if (retval)
216 return retval;
217 }
218
219
220 *found = loc_found;
221 return 0;
222
223 }
224
225 /***********************************************************
226 k5users_lookup looks for princname in file fp. Spaces
227 before the princaname (in the file ) are not ignored
228 spaces after the princname are ignored.
229
230 authorization alg:
231
232 if princname is not found return false.
233
234 if princname is found{
235 if cmd == NULL then the file entry after principal
236 name must be nothing or *
237
238 if cmd !=NULL then entry must be matched (* is ok)
239 }
240
241
242 ***********************************************************/
243 krb5_error_code
k5users_lookup(FILE * fp,char * princname,char * cmd,krb5_boolean * found,char ** out_fcmd)244 k5users_lookup(FILE *fp, char *princname, char *cmd,
245 krb5_boolean *found, char **out_fcmd)
246 {
247 krb5_error_code retval;
248 char * line;
249 char * fprinc, *fcmd;
250 char * lp;
251 char * loc_fcmd = NULL;
252 krb5_boolean loc_found = FALSE;
253
254 retval = get_line(fp, &line);
255 if (retval)
256 return retval;
257
258 while (line){
259 fprinc = get_first_token (line, &lp);
260
261 if (fprinc && (!strcmp(princname, fprinc))){
262 fcmd = get_next_token (&lp);
263
264 if ((fcmd) && (!strcmp(fcmd, PERMIT_ALL_COMMANDS))){
265 if (get_next_token(&lp) == NULL){
266 loc_fcmd =cmd ? xstrdup(cmd): NULL;
267 loc_found = TRUE;
268 }
269 free (line);
270 break;
271 }
272
273 if (cmd == NULL){
274 if (fcmd == NULL)
275 loc_found = TRUE;
276 free (line);
277 break;
278
279 }else{
280 if (fcmd != NULL) {
281 char * temp_rfcmd, *err;
282 krb5_boolean match;
283 do {
284 if(match_commands(fcmd,cmd,&match,
285 &temp_rfcmd, &err)){
286 if (auth_debug){
287 fprintf(stderr,"%s",err);
288 }
289 loc_fcmd = err;
290 break;
291 }else{
292 if (match == TRUE){
293 loc_fcmd = temp_rfcmd;
294 loc_found = TRUE;
295 break;
296 }
297 }
298
299 }while ((fcmd = get_next_token( &lp)));
300 }
301 free (line);
302 break;
303 }
304 }
305
306 free (line);
307
308 retval = get_line(fp, &line);
309 if (retval) {
310 return retval;
311 }
312 }
313
314 *out_fcmd = loc_fcmd;
315 *found = loc_found;
316 return 0;
317
318 }
319
320
321 /***********************************************
322 fcmd_resolve -
323 takes a command specified .k5users file and
324 resolves it into a full path name.
325
326 ************************************************/
327
328 krb5_boolean
fcmd_resolve(char * fcmd,char *** out_fcmd,char ** out_err)329 fcmd_resolve(char *fcmd, char ***out_fcmd, char **out_err)
330 {
331 char * err;
332 char ** tmp_fcmd = NULL;
333 char * path_ptr, *path;
334 char * lp, * tc;
335 int i=0;
336 krb5_boolean ok = FALSE;
337
338 tmp_fcmd = (char **) xcalloc (MAX_CMD, sizeof(char *));
339
340 if (*fcmd == '/'){ /* must be full path */
341 tmp_fcmd[0] = xstrdup(fcmd);
342 tmp_fcmd[1] = NULL;
343 *out_fcmd = tmp_fcmd;
344 tmp_fcmd = NULL;
345 }else{
346 /* must be either full path or just the cmd name */
347 if (strchr(fcmd, '/')){
348 asprintf(&err, _("Error: bad entry - %s in %s file, must be "
349 "either full path or just the cmd name\n"),
350 fcmd, KRB5_USERS_NAME);
351 *out_err = err;
352 goto cleanup;
353 }
354
355 #ifndef CMD_PATH
356 asprintf(&err, _("Error: bad entry - %s in %s file, since %s is just "
357 "the cmd name, CMD_PATH must be defined \n"),
358 fcmd, KRB5_USERS_NAME, fcmd);
359 *out_err = err;
360 goto cleanup;
361 #else
362
363 path = xstrdup (CMD_PATH);
364 path_ptr = path;
365
366 while ((*path_ptr == ' ') || (*path_ptr == '\t')) path_ptr ++;
367
368 tc = get_first_token (path_ptr, &lp);
369
370 if (! tc){
371 asprintf(&err, _("Error: bad entry - %s in %s file, CMD_PATH "
372 "contains no paths \n"), fcmd, KRB5_USERS_NAME);
373 *out_err = err;
374 goto cleanup;
375 }
376
377 i=0;
378 do{
379 if (*tc != '/'){ /* must be full path */
380 asprintf(&err, _("Error: bad path %s in CMD_PATH for %s must "
381 "start with '/' \n"), tc, KRB5_USERS_NAME );
382 *out_err = err;
383 goto cleanup;
384 }
385
386 tmp_fcmd[i] = xasprintf("%s/%s", tc, fcmd);
387
388 i++;
389
390 } while((tc = get_next_token (&lp)));
391
392 tmp_fcmd[i] = NULL;
393 *out_fcmd = tmp_fcmd;
394 tmp_fcmd = NULL;
395 #endif /* CMD_PATH */
396 }
397
398 ok = TRUE;
399
400 cleanup:
401 free_fcmd_list(tmp_fcmd);
402 return ok;
403 }
404
405 /********************************************
406 cmd_single - checks if cmd consists of a path
407 or a single token
408
409 ********************************************/
410
411 krb5_boolean
cmd_single(char * cmd)412 cmd_single(char *cmd)
413 {
414
415 if ( ( strrchr( cmd, '/')) == NULL){
416 return TRUE;
417 }else{
418 return FALSE;
419 }
420 }
421
422 /********************************************
423 cmd_arr_cmp_postfix - compares a command with the postfix
424 of fcmd
425 ********************************************/
426
427 int
cmd_arr_cmp_postfix(char ** fcmd_arr,char * cmd)428 cmd_arr_cmp_postfix(char **fcmd_arr, char *cmd)
429 {
430 char * temp_fcmd;
431 char *ptr;
432 int result =1;
433 int i = 0;
434
435 while(fcmd_arr[i]){
436 if ( (ptr = strrchr( fcmd_arr[i], '/')) == NULL){
437 temp_fcmd = fcmd_arr[i];
438 }else {
439 temp_fcmd = ptr + 1;
440 }
441
442 result = strcmp (temp_fcmd, cmd);
443 if (result == 0){
444 break;
445 }
446 i++;
447 }
448
449 return result;
450
451
452 }
453
454 /**********************************************
455 cmd_arr_cmp - checks if cmd matches any
456 of the fcmd entries.
457
458 **********************************************/
459
460 int
cmd_arr_cmp(char ** fcmd_arr,char * cmd)461 cmd_arr_cmp(char **fcmd_arr, char *cmd)
462 {
463 int result =1;
464 int i = 0;
465
466 while(fcmd_arr[i]){
467 result = strcmp (fcmd_arr[i], cmd);
468 if (result == 0){
469 break;
470 }
471 i++;
472 }
473 return result;
474 }
475
476
477 krb5_boolean
find_first_cmd_that_exists(char ** fcmd_arr,char ** cmd_out,char ** err_out)478 find_first_cmd_that_exists(char **fcmd_arr, char **cmd_out, char **err_out)
479 {
480 struct stat st_temp;
481 int i = 0;
482 krb5_boolean retbool= FALSE;
483 int j =0;
484 struct k5buf buf;
485
486 while(fcmd_arr[i]){
487 if (!stat (fcmd_arr[i], &st_temp )){
488 *cmd_out = xstrdup(fcmd_arr[i]);
489 retbool = TRUE;
490 break;
491 }
492 i++;
493 }
494
495 if (retbool == FALSE ){
496 k5_buf_init_dynamic(&buf);
497 k5_buf_add(&buf, _("Error: not found -> "));
498 for(j= 0; j < i; j ++)
499 k5_buf_add_fmt(&buf, " %s ", fcmd_arr[j]);
500 k5_buf_add(&buf, "\n");
501 *err_out = k5_buf_cstring(&buf);
502 if (*err_out == NULL) {
503 perror(prog_name);
504 exit(1);
505 }
506 }
507
508
509 return retbool;
510 }
511
512 /***************************************************************
513 returns 1 if there is an error, 0 if no error.
514
515 ***************************************************************/
516
517 int
match_commands(char * fcmd,char * cmd,krb5_boolean * match,char ** cmd_out,char ** err_out)518 match_commands(char *fcmd, char *cmd, krb5_boolean *match,
519 char **cmd_out, char **err_out)
520 {
521 char ** fcmd_arr = NULL;
522 char * err;
523 char * cmd_temp;
524 int result = 1;
525
526 if(fcmd_resolve(fcmd, &fcmd_arr, &err )== FALSE ){
527 *err_out = err;
528 goto cleanup;
529 }
530
531 if (cmd_single( cmd ) == TRUE){
532 if (!cmd_arr_cmp_postfix(fcmd_arr, cmd)){ /* found */
533 if (!find_first_cmd_that_exists(fcmd_arr, &cmd_temp, &err)) {
534 *err_out = err;
535 goto cleanup;
536 }
537
538 *match = TRUE;
539 *cmd_out = cmd_temp;
540 } else {
541 *match = FALSE;
542 }
543 }else{
544 if (!cmd_arr_cmp(fcmd_arr, cmd)){ /* found */
545 *match = TRUE;
546 *cmd_out = xstrdup(cmd);
547 } else{
548 *match = FALSE;
549 }
550 }
551
552 result = 0;
553
554 cleanup:
555 free_fcmd_list(fcmd_arr);
556 return result;
557 }
558
559 /*********************************************************
560 get_line - returns a line of any length. out_line
561 is set to null if eof.
562 *********************************************************/
563
564 krb5_error_code
get_line(FILE * fp,char ** out_line)565 get_line(FILE *fp, char **out_line)
566 {
567 char * line, *r, *newline , *line_ptr;
568 int chunk_count = 1;
569
570 line = (char *) xcalloc (BUFSIZ, sizeof (char ));
571 line_ptr = line;
572 line[0] = '\0';
573
574 while (( r = fgets(line_ptr, BUFSIZ , fp)) != NULL){
575 newline = strchr(line_ptr, '\n');
576 if (newline) {
577 *newline = '\0';
578 break;
579 }
580 else {
581 chunk_count ++;
582 line = xrealloc(line, chunk_count * BUFSIZ);
583
584 line_ptr = line + (BUFSIZ -1) *( chunk_count -1) ;
585 }
586 }
587
588 if ((r == NULL) && (strlen(line) == 0)) {
589 *out_line = NULL;
590 }
591 else{
592 *out_line = line;
593 }
594
595 return 0;
596 }
597
598 /*******************************************************
599 get_first_token -
600 Expects a '\0' terminated input line .
601 If there are any spaces before the first token, they
602 will be returned as part of the first token.
603
604 Note: this routine reuses the space pointed to by line
605 ******************************************************/
606
607 char *
get_first_token(char * line,char ** lnext)608 get_first_token(char *line, char **lnext)
609 {
610
611 char * lptr, * out_ptr;
612
613
614 out_ptr = line;
615 lptr = line;
616
617 while (( *lptr == ' ') || (*lptr == '\t')) lptr ++;
618
619 if (strlen(lptr) == 0) return NULL;
620
621 while (( *lptr != ' ') && (*lptr != '\t') && (*lptr != '\0')) lptr ++;
622
623 if (*lptr == '\0'){
624 *lnext = lptr;
625 } else{
626 *lptr = '\0';
627 *lnext = lptr + 1;
628 }
629
630 return out_ptr;
631 }
632 /**********************************************************
633 get_next_token -
634 returns the next token pointed to by *lnext.
635 returns NULL if there is no more tokens.
636 Note: that this function modifies the stream
637 pointed to by *lnext and does not allocate
638 space for the returned tocken. It also advances
639 lnext to the next tocken.
640 **********************************************************/
641
642 char *
get_next_token(char ** lnext)643 get_next_token (char **lnext)
644 {
645 char * lptr, * out_ptr;
646
647
648 lptr = *lnext;
649
650 while (( *lptr == ' ') || (*lptr == '\t')) lptr ++;
651
652 if (strlen(lptr) == 0) return NULL;
653
654 out_ptr = lptr;
655
656 while (( *lptr != ' ') && (*lptr != '\t') && (*lptr != '\0')) lptr ++;
657
658 if (*lptr == '\0'){
659 *lnext = lptr;
660 } else{
661 *lptr = '\0';
662 *lnext = lptr + 1;
663 }
664
665 return out_ptr;
666 }
667
668 void
init_auth_names(char * pw_dir)669 init_auth_names(char *pw_dir)
670 {
671 const char *sep;
672 int r1, r2;
673
674 sep = ((strlen(pw_dir) == 1) && (*pw_dir == '/')) ? "" : "/";
675 r1 = snprintf(k5login_path, sizeof(k5login_path), "%s%s%s",
676 pw_dir, sep, KRB5_LOGIN_NAME);
677 r2 = snprintf(k5users_path, sizeof(k5users_path), "%s%s%s",
678 pw_dir, sep, KRB5_USERS_NAME);
679 if (SNPRINTF_OVERFLOW(r1, sizeof(k5login_path)) ||
680 SNPRINTF_OVERFLOW(r2, sizeof(k5users_path))) {
681 fprintf(stderr, _("home directory name `%s' too long, can't search "
682 "for .k5login\n"), pw_dir);
683 exit (1);
684 }
685 }
686