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