xref: /freebsd/crypto/krb5/src/clients/ksu/authorization.c (revision f1c4c3daccbaf3820f0e2224de53df12fc952fcc)
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