Imported Upstream version 1.15.1
[platform/upstream/krb5.git] / src / clients / ksu / authorization.c
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 writen by:  Ari Medvinsky, ari@isi.edu
27  */
28
29 #include "ksu.h"
30
31 static void auth_cleanup (FILE *, FILE *, char *);
32
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
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 0
127     if (cmd){
128         if(k5users_flag){
129             return 0; /* if  kusers does not exist -> done */
130         }else{
131             if(retval = k5users_lookup(users_fp,princname,
132                                        cmd,&retbool,out_fcmd)){
133                 auth_cleanup(users_fp, login_fp, princname);
134                 return retval;
135             }else{
136                 *ok =retbool;
137                 return retval;
138             }
139         }
140     }
141 #endif
142
143     /* if either file exists,
144        first see if the principal is in the login in file,
145        if it's not there check the k5users file */
146
147     if (!k5login_flag){
148         if (auth_debug)
149             fprintf(stderr,
150                     "In krb5_authorization: principal to be authorized %s\n",
151                     princname);
152
153         retval = k5login_lookup(login_fp,  princname, &retbool);
154         if (retval) {
155             auth_cleanup(users_fp, login_fp, princname);
156             return retval;
157         }
158         if (retbool) {
159             if (cmd)
160                 *out_fcmd = xstrdup(cmd);
161         }
162     }
163
164     if ((!k5users_flag) && (retbool == FALSE) ){
165         retval = k5users_lookup (users_fp, princname,
166                                  cmd, &retbool, out_fcmd);
167         if(retval) {
168             auth_cleanup(users_fp, login_fp, princname);
169             return retval;
170         }
171     }
172
173     if (k5login_flag && k5users_flag){
174
175         char * kuser =  (char *) xcalloc (strlen(princname), sizeof(char));
176         if (!(krb5_aname_to_localname(context, principal,
177                                       strlen(princname), kuser))
178             && (strcmp(kuser, luser) == 0)) {
179             retbool = TRUE;
180         }
181
182         free(kuser);
183     }
184
185     *ok =retbool;
186     auth_cleanup(users_fp, login_fp, princname);
187     return 0;
188 }
189
190 /***********************************************************
191 k5login_lookup looks for princname in file fp. Spaces
192 before the princaname (in the file ) are not ignored
193 spaces after the princname are ignored. If there are
194 any tokens after the principal name  FALSE is returned.
195
196 ***********************************************************/
197
198 krb5_error_code k5login_lookup (fp, princname, found)
199     FILE *fp;
200     char *princname;
201     krb5_boolean *found;
202 {
203
204     krb5_error_code retval;
205     char * line;
206     char * fprinc;
207     char * lp;
208     krb5_boolean loc_found = FALSE;
209
210     retval = get_line(fp, &line);
211     if (retval)
212         return retval;
213
214     while (line){
215         fprinc = get_first_token (line, &lp);
216
217         if (fprinc && (!strcmp(princname, fprinc))){
218             if( get_next_token (&lp) ){
219                 free (line);
220                 break;  /* nothing should follow princname*/
221             }
222             else{
223                 loc_found = TRUE;
224                 free (line);
225                 break;
226             }
227         }
228
229         free (line);
230
231         retval = get_line(fp, &line);
232         if (retval)
233             return retval;
234     }
235
236
237     *found = loc_found;
238     return 0;
239
240 }
241
242 /***********************************************************
243 k5users_lookup looks for princname in file fp. Spaces
244 before the princaname (in the file ) are not ignored
245 spaces after the princname are ignored.
246
247 authorization alg:
248
249 if princname is not found return false.
250
251 if princname is found{
252          if cmd == NULL then the file entry after principal
253                         name must be nothing or *
254
255          if cmd !=NULL  then entry must be matched (* is ok)
256 }
257
258
259 ***********************************************************/
260 krb5_error_code k5users_lookup (fp, princname, cmd, found, out_fcmd)
261     FILE *fp;
262     char *princname;
263     char *cmd;
264     krb5_boolean *found;
265     char **out_fcmd;
266 {
267     krb5_error_code retval;
268     char * line;
269     char * fprinc, *fcmd;
270     char * lp;
271     char * loc_fcmd = NULL;
272     krb5_boolean loc_found = FALSE;
273
274     retval = get_line(fp, &line);
275     if (retval)
276         return retval;
277
278     while (line){
279         fprinc = get_first_token (line, &lp);
280
281         if (fprinc && (!strcmp(princname, fprinc))){
282             fcmd = get_next_token (&lp);
283
284             if ((fcmd) && (!strcmp(fcmd, PERMIT_ALL_COMMANDS))){
285                 if (get_next_token(&lp) == NULL){
286                     loc_fcmd =cmd ? xstrdup(cmd): NULL;
287                     loc_found = TRUE;
288                 }
289                 free (line);
290                 break;
291             }
292
293             if (cmd == NULL){
294                 if (fcmd == NULL)
295                     loc_found = TRUE;
296                 free (line);
297                 break;
298
299             }else{
300                 if (fcmd != NULL) {
301                     char * temp_rfcmd, *err;
302                     krb5_boolean match;
303                     do {
304                         if(match_commands(fcmd,cmd,&match,
305                                           &temp_rfcmd, &err)){
306                             if (auth_debug){
307                                 fprintf(stderr,"%s",err);
308                             }
309                             loc_fcmd = err;
310                             break;
311                         }else{
312                             if (match == TRUE){
313                                 loc_fcmd = temp_rfcmd;
314                                 loc_found = TRUE;
315                                 break;
316                             }
317                         }
318
319                     }while ((fcmd = get_next_token( &lp)));
320                 }
321                 free (line);
322                 break;
323             }
324         }
325
326         free (line);
327
328         retval = get_line(fp, &line);
329         if (retval) {
330             return retval;
331         }
332     }
333
334     *out_fcmd = loc_fcmd;
335     *found = loc_found;
336     return 0;
337
338 }
339
340
341 /***********************************************
342 fcmd_resolve -
343 takes a command specified .k5users file and
344 resolves it into a full path name.
345
346 ************************************************/
347
348 krb5_boolean fcmd_resolve(fcmd, out_fcmd, out_err)
349     char *fcmd;
350     char ***out_fcmd;
351     char **out_err;
352 {
353     char * err;
354     char ** tmp_fcmd;
355     char * path_ptr, *path;
356     char * lp, * tc;
357     int i=0;
358
359     tmp_fcmd = (char **) xcalloc (MAX_CMD, sizeof(char *));
360
361     if (*fcmd == '/'){  /* must be full path */
362         tmp_fcmd[0] = xstrdup(fcmd);
363         tmp_fcmd[1] = NULL;
364         *out_fcmd = tmp_fcmd;
365         return TRUE;
366     }else{
367         /* must be either full path or just the cmd name */
368         if (strchr(fcmd, '/')){
369             asprintf(&err, _("Error: bad entry - %s in %s file, must be "
370                              "either full path or just the cmd name\n"),
371                      fcmd, KRB5_USERS_NAME);
372             *out_err = err;
373             return FALSE;
374         }
375
376 #ifndef CMD_PATH
377         asprintf(&err, _("Error: bad entry - %s in %s file, since %s is just "
378                          "the cmd name, CMD_PATH must be defined \n"),
379                  fcmd, KRB5_USERS_NAME, fcmd);
380         *out_err = err;
381         return FALSE;
382 #else
383
384         path = xstrdup (CMD_PATH);
385         path_ptr = path;
386
387         while ((*path_ptr == ' ') || (*path_ptr == '\t')) path_ptr ++;
388
389         tc = get_first_token (path_ptr, &lp);
390
391         if (! tc){
392             asprintf(&err, _("Error: bad entry - %s in %s file, CMD_PATH "
393                              "contains no paths \n"), fcmd, KRB5_USERS_NAME);
394             *out_err = err;
395             return FALSE;
396         }
397
398         i=0;
399         do{
400             if (*tc != '/'){  /* must be full path */
401                 asprintf(&err, _("Error: bad path %s in CMD_PATH for %s must "
402                                  "start with '/' \n"), tc, KRB5_USERS_NAME );
403                 *out_err = err;
404                 return FALSE;
405             }
406
407             tmp_fcmd[i] = xasprintf("%s/%s", tc, fcmd);
408
409             i++;
410
411         } while((tc = get_next_token (&lp)));
412
413         tmp_fcmd[i] = NULL;
414         *out_fcmd = tmp_fcmd;
415         return TRUE;
416
417 #endif /* CMD_PATH */
418     }
419 }
420
421 /********************************************
422 cmd_single - checks if cmd consists of a path
423              or a single token
424
425 ********************************************/
426
427 krb5_boolean cmd_single(cmd)
428     char * cmd;
429 {
430
431     if ( ( strrchr( cmd, '/')) ==  NULL){
432         return TRUE;
433     }else{
434         return FALSE;
435     }
436 }
437
438 /********************************************
439 cmd_arr_cmp_postfix - compares a command with the postfix
440          of fcmd
441 ********************************************/
442
443 int cmd_arr_cmp_postfix(fcmd_arr, cmd)
444     char **fcmd_arr;
445     char *cmd;
446 {
447     char  * temp_fcmd;
448     char *ptr;
449     int result =1;
450     int i = 0;
451
452     while(fcmd_arr[i]){
453         if ( (ptr = strrchr( fcmd_arr[i], '/')) ==  NULL){
454             temp_fcmd = fcmd_arr[i];
455         }else {
456             temp_fcmd = ptr + 1;
457         }
458
459         result = strcmp (temp_fcmd, cmd);
460         if (result == 0){
461             break;
462         }
463         i++;
464     }
465
466     return result;
467
468
469 }
470
471 /**********************************************
472 cmd_arr_cmp - checks if cmd matches any
473               of the fcmd entries.
474
475 **********************************************/
476
477 int cmd_arr_cmp (fcmd_arr, cmd)
478     char **fcmd_arr;
479     char *cmd;
480 {
481     int result =1;
482     int i = 0;
483
484     while(fcmd_arr[i]){
485         result = strcmp (fcmd_arr[i], cmd);
486         if (result == 0){
487             break;
488         }
489         i++;
490     }
491     return result;
492 }
493
494
495 krb5_boolean find_first_cmd_that_exists(fcmd_arr, cmd_out, err_out)
496     char **fcmd_arr;
497     char **cmd_out;
498     char **err_out;
499 {
500     struct stat st_temp;
501     int i = 0;
502     krb5_boolean retbool= FALSE;
503     int j =0;
504     struct k5buf buf;
505
506     while(fcmd_arr[i]){
507         if (!stat (fcmd_arr[i], &st_temp )){
508             *cmd_out = xstrdup(fcmd_arr[i]);
509             retbool = TRUE;
510             break;
511         }
512         i++;
513     }
514
515     if (retbool == FALSE ){
516         k5_buf_init_dynamic(&buf);
517         k5_buf_add(&buf, _("Error: not found -> "));
518         for(j= 0; j < i; j ++)
519             k5_buf_add_fmt(&buf, " %s ", fcmd_arr[j]);
520         k5_buf_add(&buf, "\n");
521         if (k5_buf_status(&buf) != 0) {
522             perror(prog_name);
523             exit(1);
524         }
525         *err_out = buf.data;
526     }
527
528
529     return retbool;
530 }
531
532 /***************************************************************
533 returns 1 if there is an error, 0 if no error.
534
535 ***************************************************************/
536
537 int match_commands (fcmd, cmd, match, cmd_out, err_out)
538     char *fcmd;
539     char *cmd;
540     krb5_boolean *match;
541     char **cmd_out;
542     char **err_out;
543 {
544     char ** fcmd_arr;
545     char * err;
546     char * cmd_temp;
547
548     if(fcmd_resolve(fcmd, &fcmd_arr, &err )== FALSE ){
549         *err_out = err;
550         return 1;
551     }
552
553     if (cmd_single( cmd ) == TRUE){
554         if (!cmd_arr_cmp_postfix(fcmd_arr, cmd)){ /* found */
555
556             if(find_first_cmd_that_exists( fcmd_arr,&cmd_temp,&err)== TRUE){
557                 *match = TRUE;
558                 *cmd_out = cmd_temp;
559                 return 0;
560             }else{
561                 *err_out = err;
562                 return 1;
563             }
564         }else{
565             *match = FALSE;
566             return 0;
567         }
568     }else{
569         if (!cmd_arr_cmp(fcmd_arr, cmd)){  /* found */
570             *match = TRUE;
571             *cmd_out = xstrdup(cmd);
572             return 0;
573         } else{
574             *match = FALSE;
575             return 0;
576         }
577     }
578
579 }
580
581 /*********************************************************
582    get_line - returns a line of any length.  out_line
583               is set to null if eof.
584 *********************************************************/
585
586 krb5_error_code get_line (fp, out_line)
587 /* IN */
588     FILE *fp;
589     /* OUT */
590     char **out_line;
591 {
592     char * line, *r, *newline , *line_ptr;
593     int chunk_count = 1;
594
595     line = (char *) xcalloc (BUFSIZ, sizeof (char ));
596     line_ptr = line;
597     line[0] = '\0';
598
599     while (( r = fgets(line_ptr, BUFSIZ , fp)) != NULL){
600         newline = strchr(line_ptr, '\n');
601         if (newline) {
602             *newline = '\0';
603             break;
604         }
605         else {
606             chunk_count ++;
607             if(!( line = (char *) realloc( line,
608                                            chunk_count * sizeof(char) * BUFSIZ))){
609                 return  ENOMEM;
610             }
611
612             line_ptr = line + (BUFSIZ -1) *( chunk_count -1) ;
613         }
614     }
615
616     if ((r == NULL) && (strlen(line) == 0)) {
617         *out_line = NULL;
618     }
619     else{
620         *out_line = line;
621     }
622
623     return 0;
624 }
625
626 /*******************************************************
627 get_first_token -
628 Expects a '\0' terminated input line .
629 If there are any spaces before the first token, they
630 will be returned as part of the first token.
631
632 Note: this routine reuses the space pointed to by line
633 ******************************************************/
634
635 char *  get_first_token (line, lnext)
636     char *line;
637     char **lnext;
638 {
639
640     char * lptr, * out_ptr;
641
642
643     out_ptr = line;
644     lptr = line;
645
646     while (( *lptr == ' ') || (*lptr == '\t')) lptr ++;
647
648     if (strlen(lptr) == 0) return NULL;
649
650     while (( *lptr != ' ') && (*lptr != '\t') && (*lptr != '\0')) lptr ++;
651
652     if (*lptr == '\0'){
653         *lnext = lptr;
654     } else{
655         *lptr = '\0';
656         *lnext = lptr + 1;
657     }
658
659     return out_ptr;
660 }
661 /**********************************************************
662 get_next_token -
663 returns the next token pointed to by *lnext.
664 returns NULL if there is no more tokens.
665 Note: that this function modifies the stream
666       pointed to by *lnext and does not allocate
667       space for the returned tocken. It also advances
668       lnext to the next tocken.
669 **********************************************************/
670
671 char *  get_next_token (lnext)
672     char **lnext;
673 {
674     char * lptr, * out_ptr;
675
676
677     lptr = *lnext;
678
679     while (( *lptr == ' ') || (*lptr == '\t')) lptr ++;
680
681     if (strlen(lptr) == 0) return NULL;
682
683     out_ptr = lptr;
684
685     while (( *lptr != ' ') && (*lptr != '\t') && (*lptr != '\0')) lptr ++;
686
687     if (*lptr == '\0'){
688         *lnext = lptr;
689     } else{
690         *lptr = '\0';
691         *lnext = lptr + 1;
692     }
693
694     return out_ptr;
695 }
696
697 static void auth_cleanup(users_fp, login_fp, princname)
698     FILE *users_fp;
699     FILE *login_fp;
700     char *princname;
701 {
702
703     free (princname);
704     if (users_fp)
705         fclose(users_fp);
706     if (login_fp)
707         fclose(login_fp);
708 }
709
710 void init_auth_names(pw_dir)
711     char *pw_dir;
712 {
713     const char *sep;
714     int r1, r2;
715
716     sep = ((strlen(pw_dir) == 1) && (*pw_dir == '/')) ? "" : "/";
717     r1 = snprintf(k5login_path, sizeof(k5login_path), "%s%s%s",
718                   pw_dir, sep, KRB5_LOGIN_NAME);
719     r2 = snprintf(k5users_path, sizeof(k5users_path), "%s%s%s",
720                   pw_dir, sep, KRB5_USERS_NAME);
721     if (SNPRINTF_OVERFLOW(r1, sizeof(k5login_path)) ||
722         SNPRINTF_OVERFLOW(r2, sizeof(k5users_path))) {
723         fprintf(stderr, _("home directory name `%s' too long, can't search "
724                           "for .k5login\n"), pw_dir);
725         exit (1);
726     }
727 }