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