xref: /illumos-gate/usr/src/cmd/bnu/permission.c (revision 3d393ee6c37fa10ac512ed6d36109ad616dc7c1a)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved  	*/
29 
30 #pragma ident	"%Z%%M%	%I%	%E% SMI"
31 
32 #ifndef UUCHECK
33 #include "uucp.h"
34 #endif
35 
36 
37 /*  field array indexes for PERMISSIONS parameters */
38 #define U_LOGNAME	0
39 #define U_MACHINE	1
40 #define U_CALLBACK	2
41 #define U_REQUEST	3
42 #define U_SENDFILES	4
43 #define U_READPATH	5
44 #define U_WRITEPATH	6
45 #define U_NOREADPATH	7
46 #define U_NOWRITEPATH	8
47 #define U_MYNAME	9
48 #define U_COMMANDS	10
49 #define U_VALIDATE	11
50 #define U_PUBDIR	12
51 #define U_DIRECT	13
52 #define U_ALIAS		14
53 #define U_PATH		15
54 /*  NUMFLDS should be one more than the highest U_ value */
55 #define NUMFLDS		16
56 
57 /* fields found in PERMISSIONS for requested system/login */
58 static char *_Flds[NUMFLDS];
59 
60 /* keyword/value structure */
61 struct keywords {
62 	char* kword;
63 	int kvalue;
64 };
65 static struct keywords _Kwords[] = {
66 	{"LOGNAME", U_LOGNAME},
67 	{"MACHINE", U_MACHINE},
68 	{"CALLBACK", U_CALLBACK},
69 	{"REQUEST", U_REQUEST},
70 	{"SENDFILES", U_SENDFILES},
71 	{"READ", U_READPATH},
72 	{"WRITE", U_WRITEPATH},
73 	{"NOREAD", U_NOREADPATH},
74 	{"NOWRITE", U_NOWRITEPATH},
75 	{"MYNAME", U_MYNAME},
76 	{"COMMANDS", U_COMMANDS},
77 	{"VALIDATE", U_VALIDATE},
78 	{"PUBDIR", U_PUBDIR},
79 	{"DIRECT", U_DIRECT},
80 	{"ALIAS", U_ALIAS},
81 	{"PATH", U_PATH},
82 };
83 
84 #define MAXCMDS		30
85 #define MAXPATHS	20
86 
87 /* for all options on paths - read, write, noread, nowrite */
88 /* NB: all pointers assumed to point to static data */
89 static char *_RPaths[MAXPATHS+1];
90 static char *_WPaths[MAXPATHS+1];
91 static char *_NoRPaths[MAXPATHS+1];
92 static char *_NoWPaths[MAXPATHS+1];
93 static char *_Commands[MAXCMDS+1];
94 static char _Cmd_defaults[BUFSIZ];
95 
96 /* option variables */
97 static int _Request;	/* TRUE can request, FALSE can not request files */
98 static int _Switch;	/* FALSE requires a call back to send any files */
99 static int _CallBack;	/* TRUE for call back for any transaction */
100 static int _NoSpool;	/* TRUE if delivering directly to destination file */
101 static char _MyName[MAXBASENAME+1];	/* Myname from PERMISSIONS file */
102 /* NB: _Pubdir and _Path assumed to point to dynamic data */
103 static char *_Pubdir = NULL;		/* PUBDIR from PERMISSIONS file */
104 static char *_Path = NULL;		/* PATH from PERMISSIONS file */
105 
106 struct name_value
107 {
108 	char *name;
109 	char *value;
110 };
111 
112 /* file pointer for PERMISSIONS */
113 static FILE *Fp = NULL;
114 
115 /* functions */
116 extern char *next_token(), *nextarg();
117 extern int parse_tokens(), canPath(), mkdirs();
118 static void fillFlds();
119 static void fillList();
120 static int cmdMatch(), listMatch(), nameMatch(),
121 	userFind(), validateFind();
122 
123 int
124 noSpool()
125 {
126 	return(_NoSpool);
127 }
128 
129 /*
130  * fill in fields for login name
131  * name - the login id
132  * rmtname - remote system name
133  *
134  * return:
135  *	0 -> found login name
136  *	FAIL -> did not find login
137  */
138 
139 int
140 logFind(name, rmtname)
141 char *name, *rmtname;
142 {
143 	int ret;
144 	DEBUG(5, "logFind called (name: %s, ", name);
145 	DEBUG(5, "rmtname: %s)\n", rmtname);
146 
147 	ret = validateFind (rmtname);
148 	if (ret == SUCCESS) { /* found VALIDATE entry */
149 	    ret = userFind (name, rmtname, U_VALIDATE);
150 	    if (ret) {
151 		DEBUG(5, "machine/login match failed%s", "");
152 		return(FAIL);
153 	    }
154 	}
155 	else
156 	    ret = userFind (name, "", U_LOGNAME);
157 
158 	DEBUG(7, "_Request (%s), ",
159 	    requestOK() ? "TRUE" : "FALSE");
160 	DEBUG(7, "_Switch (%s), ",
161 	    switchRole() ? "TRUE" : "FALSE");
162 	DEBUG(7, "_CallBack (%s), ",
163 	    callBack() ? "TRUE" : "FALSE");
164 	DEBUG(7, "_MyName (%s), ", _MyName);
165 	DEBUG(7, "_NoSpool (%s), ",
166 	    noSpool() ? "TRUE" : "FALSE");
167 	return(ret);
168 }
169 
170 /*
171  * fill in fields for machine name
172  * return:
173  *	0 -> found machine name
174  *	FAIL -> did not find machine
175  */
176 
177 int
178 mchFind(name)
179 char *name;
180 {
181 	int i, ret;
182 	DEBUG(5, "mchFind called (%s)\n", name);
183 	if ( (ret = userFind (name, "", U_MACHINE)) == FAIL)
184 	    /* see if there is a default line */
185 	    (void) userFind ("OTHER", "", U_MACHINE);
186 
187 	/*  mchFind is from MASTER mode - switch role is always ok */
188 	_Switch = TRUE;
189 
190 	DEBUG(7, "_Request (%s), ",
191 	    requestOK() ? "TRUE" : "FALSE");
192 	DEBUG(7, "_Switch (%s), ",
193 	    switchRole() ? "TRUE" : "FALSE");
194 	DEBUG(7, "_CallBack (%s), ",
195 	    callBack() ? "TRUE" : "FALSE");
196 	DEBUG(7, "_MyName (%s), ", _MyName);
197 	DEBUG(7, "_NoSpool (%s), ",
198 	    noSpool() ? "TRUE" : "FALSE");
199 	for (i=0; _Commands[i] != NULL; i++)
200 	    DEBUG(7, "_Commands %s\n",  _Commands[i]);
201 	return(ret);
202 }
203 
204 /*
205  * this function will find a login name in the LOGNAME
206  * field.
207  * input:
208  *	name	-> who the remote says he/she is
209  * return:
210  *	SUCCESS	-> found
211  *	FAIL	-> not found
212  */
213 static int
214 nameMatch(name, fld)
215 char *name, *fld;
216 {
217 	char *arg;
218 
219 	if (fld == NULL)
220 	    return(FAIL);
221 
222 	while (*fld) {
223 	    fld = nextarg(fld, &arg);
224 	    if (EQUALS(arg, name))
225 		return(SUCCESS);
226 	}
227 	return (FAIL);
228 }
229 
230 
231 /*
232  * interpret the _Flds options and set the option variables
233  */
234 static void
235 fillFlds()
236 {
237 
238 	if (_Flds[U_REQUEST] != NULL) {
239 		if (EQUALS(_Flds[U_REQUEST], "yes"))
240 			_Request = TRUE;
241 		else
242 			_Request = FALSE;
243 	}
244 
245 	if (_Flds[U_SENDFILES] != NULL) {
246 		if (EQUALS(_Flds[U_SENDFILES], "yes"))
247 			_Switch = TRUE;
248 		else
249 			_Switch = FALSE;
250 	}
251 
252 	if (_Flds[U_CALLBACK] != NULL) {
253 		if (EQUALS(_Flds[U_CALLBACK], "yes"))
254 			_CallBack = TRUE;
255 		else
256 			_CallBack = FALSE;
257 	}
258 
259 	if (_Flds[U_DIRECT] != NULL) {
260 		if (EQUALS(_Flds[U_DIRECT], "yes"))
261 			_NoSpool = TRUE;
262 		else
263 			_NoSpool = FALSE;
264 	}
265 
266 	if (_Flds[U_MYNAME] != NULL) {
267 		strncpy(_MyName, _Flds[U_MYNAME], MAXBASENAME);
268 		_MyName[MAXBASENAME] = NULLCHAR;
269 	}
270 
271 	if (_Flds[U_PUBDIR] != NULL) {
272 		if (_Pubdir != NULL)
273 		    free(_Pubdir);	/* get rid of previous one */
274 		_Pubdir = strdup(_Flds[U_PUBDIR]);
275 #ifndef UUCHECK
276 		ASSERT(_Pubdir != NULL, Ct_ALLOCATE, _Flds[U_PUBDIR], 0);
277 #else /* UUCHECK */
278 		if (_Pubdir == NULL) {
279 		    perror(gettext("malloc() error"));
280 		    exit(1);
281 		}
282 #endif /* UUCHECK */
283 		Pubdir = _RPaths[0] = _WPaths[0] = _Pubdir; /* reset default */
284 	}
285 
286 	if (_Flds[U_PATH] != NULL) {
287 		if (_Path != NULL)
288 		    free(_Path);	/* get rid of previous one */
289 		_Path = strdup(_Flds[U_PATH]);
290 #ifndef UUCHECK
291 		ASSERT(_Path != NULL, Ct_ALLOCATE, _Flds[U_PATH], 0);
292 #else /* UUCHECK */
293 		if (_Path == NULL) {
294 		    perror(gettext("malloc() error"));
295 		    exit(1);
296 		}
297 #endif /* UUCHECK */
298 	}
299 
300 	return;
301 }
302 
303 /*
304  * fill in the list vector for the system/login
305  * input:
306  *	type - list type (read, write, noread, nowrite, command)
307  * output:
308  *	list - filled in with items.
309  * return:
310  *	number of items in list
311  */
312 static void
313 fillList(type, list)
314 int type;
315 char *list[];
316 {
317 	char *p;
318 	int num;
319 	int maxlist = 0;
320 
321 	p = _Flds[type];
322 
323 	/* find list limit */
324 	if (type == U_READPATH || type == U_WRITEPATH
325 	 || type == U_NOREADPATH || type == U_NOWRITEPATH)
326 		maxlist = MAXPATHS;
327 	else if (type == U_COMMANDS)
328 		maxlist = MAXCMDS;
329 
330 	if (p == NULL || !*p) {
331 		 /* no names specified, default already setup */
332 		return;
333 	}
334 
335 	num = 0;
336 	while (*p && num < maxlist) {
337 		list[num] = p;
338 		if (*p == ':') {	/* null path */
339 			*p++ = NULLCHAR;
340 			continue;
341 		}
342 		while (*p && *p != ':')
343 			p++;
344 		if (*p == ':')
345 			*p++ = NULLCHAR;
346 		DEBUG(7, "list (%s) ", list[num]);
347 		num++;
348 	}
349 	DEBUG(7, "num = %d\n", num);
350 	list[num] = NULL;
351 	return;
352 }
353 
354 /*
355  * Find the line of PERMISSIONS for login.
356  * The search is determined by the type field
357  * (type=U_LOGNAME, U_MACHINE or U_VALIDATE)
358  * For U_LOGNAME:
359  *	search for "name" in a LOGNAME= option
360  * For U_MACHINE:
361  *	search for "name" in a MACHINE= option
362  * For U_VALIDATE:
363  *	search for "rmtname" in a VALIDATE= option and
364  *	for the same entry see if "name" is in the LOGNAME= option
365  * input:
366  *	name -> search name
367  *	logname -> for validate entry
368  *	type -> U_MACHINE or U_LOGNAME
369  * output:
370  *	The global values of all options will be set
371  *	(e.g. _RPaths, _WPaths,  _Request, ...)
372  * return:
373  *	0 -> ok
374  *	FAIL -> no match found
375  */
376 static int
377 userFind(name, rmtname, type)
378 char *name, *rmtname;
379 int type;
380 {
381 	char *p, *arg, *buf = NULL;
382 	static char default_buf[BUFSIZ];
383 
384 	if (name != NULL && strcmp(name, "DEFAULT") != 0) {
385 		/* call ourself recursively to set defaults */
386 		(void) userFind("DEFAULT", "", U_MACHINE);
387 	} else {
388 		/*
389 		 * Handle case where looking for DEFAULT entry.
390 		 * First initialize all defaults to their "base"
391 		 * values.  Then the DEFAULT entry, if found,
392 		 * will override these settings.
393 		 */
394 		_Request = FALSE;
395 		_CallBack = FALSE;
396 		_Switch = FALSE;
397 		_NoSpool = FALSE;
398 		_MyName[0] = NULLCHAR;
399 		_RPaths[0] = _WPaths[0] = PUBDIR;	/* default is public */
400 		_RPaths[1] = _WPaths[1] = NULLCHAR;
401 		_NoRPaths[0] = NULLCHAR;
402 		_NoWPaths[0] = NULLCHAR;
403 		if (_Pubdir != NULL)
404 			free(_Pubdir);
405 		Pubdir = _Pubdir = strdup(PUBDIR);
406 		if (_Path != NULL)
407 			free(_Path);
408 		_Path = strdup(PATH);
409 		/* set up Commands defaults */
410 		_Flds[U_COMMANDS] = strcpy(_Cmd_defaults, DEFAULTCMDS);
411 		fillList(U_COMMANDS, _Commands);
412 		/*
413 		 * put defaults we read in in here so they're not overwritten
414 		 * by non-DEFAULT entries.
415 		 */
416 		buf = default_buf;
417 	}
418 
419 	if (name == NULL)	/* use defaults */
420 		return(0);	/* I don't think this will ever happen */
421 
422 	if ( (Fp = fopen(PERMISSIONS, "r")) == NULL) {
423 		DEBUG(5, "can't open %s\n", PERMISSIONS);
424 		return(FAIL);
425 	}
426 
427 	for (;;) {
428 	    if (parse_tokens (_Flds, buf) != 0) {
429 		(void) fclose(Fp);
430 		DEBUG(5, "name (%s) not found; return FAIL\n", name);
431 		return(FAIL);
432 	    }
433 
434 	    p = _Flds[type];
435 	    while (p && *p) {
436 		p = nextarg(p, &arg);
437 		switch (type) {
438 		case U_VALIDATE:
439 		    if (EQUALS(arg, rmtname)
440 			&& nameMatch(name, _Flds[U_LOGNAME])==SUCCESS)
441 				break;
442 		    continue;
443 
444 		case U_LOGNAME:
445 		    if (EQUALS(arg, name))
446 				break;
447 		    continue;
448 
449 		case U_MACHINE:
450 		    if (EQUALSN(arg, name, MAXBASENAME))
451 				break;
452 		    continue;
453 		}
454 
455 		(void) fclose(Fp);
456 		fillFlds();
457 
458 		/* fill in path lists */
459 		fillList(U_READPATH, _RPaths);
460 		fillList(U_WRITEPATH, _WPaths);
461 		if (!requestOK())
462 		    _Flds[U_NOREADPATH] = "/";
463 		fillList(U_NOREADPATH, _NoRPaths);
464 		fillList(U_NOWRITEPATH, _NoWPaths);
465 
466 		/* fill in command list */
467 		fillList(U_COMMANDS, _Commands);
468 
469 		return(0);
470 	    }
471 	}
472 }
473 
474 /*
475  * see if name is in a VALIDATE option
476  * return:
477  *	FAIL -> not found
478  *	SUCCESS -> found
479  */
480 static int
481 validateFind(name)
482 char *name;
483 {
484 
485 	if ( (Fp = fopen(PERMISSIONS, "r")) == NULL) {
486 		DEBUG(5, "can't open %s\n", PERMISSIONS);
487 		return(FAIL);
488 	}
489 
490 	for (;;) {
491 	    if (parse_tokens (_Flds, NULL) != 0) {
492 		DEBUG(5, "validateFind (%s) FAIL\n", name);
493 		(void) fclose(Fp);
494 		return(FAIL);
495 	    }
496 
497 	    if (_Flds[U_VALIDATE] == NULL)
498 		continue;
499 	    if (nameMatch(name, _Flds[U_VALIDATE])==SUCCESS) {
500 		(void) fclose(Fp);
501 		return (SUCCESS);
502 	    }
503 	}
504 
505 }
506 
507 /*
508  * see if name is in an ALIAS option
509  * return:
510  *	NULL -> not found
511  *	otherwise -> machine name
512  */
513 char *
514 aliasFind(name)
515 char *name;
516 {
517 
518 	if ( (Fp = fopen(PERMISSIONS, "r")) == NULL) {
519 		DEBUG(5, "can't open %s\n", PERMISSIONS);
520 		return(NULL);
521 	}
522 
523 	for (;;) {
524 	    if (parse_tokens (_Flds, NULL) != 0) {
525 		DEBUG(5, "aliasFind (%s) FAIL\n", name);
526 		(void) fclose(Fp);
527 		return(NULL);
528 	    }
529 
530 	    if (_Flds[U_ALIAS] == NULL)
531 		continue;
532 	    if (nameMatch(name, _Flds[U_ALIAS])==SUCCESS) {
533 		(void) fclose(Fp);
534 #ifndef UUCHECK
535 		ASSERT(strchr(_Flds[U_MACHINE], ':') == NULL,
536 		    "PERMISSIONS file: ALIAS is one-to-many:",
537 		    _Flds[U_MACHINE], 0);
538 #else /* UUCHECK */
539 		if (strchr(_Flds[U_MACHINE], ':') != NULL) {
540 		    printf(gettext("ALIAS is one-to-many: %s -> %s\n"),
541 			name, _Flds[U_MACHINE]);
542 		    return(NULL);
543 		}
544 #endif /* UUCHECK */
545 		return(_Flds[U_MACHINE]);
546 	    }
547 	}
548 
549 }
550 
551 /*
552  * parse a line in PERMISSIONS and return a vector
553  * of fields (flds)
554  *
555  * return:
556  *	0 - OK
557  *	EOF - at end of file
558  */
559 int
560 parse_tokens(flds, buf)
561 char *flds[];
562 char *buf;
563 {
564 	int i;
565 	char *p;
566 	struct name_value pair;
567 	static char _line[BUFSIZ];
568 	char *line = buf;
569 
570 	if (buf == NULL)
571 		line = _line;	/* if no buffer specified, use default */
572 	/* initialize defaults  in case parameter is not specified */
573 	for (i=0;i<NUMFLDS;i++)
574 		flds[i] = NULL;
575 
576 	if (getuline(Fp, line) == 0)
577 		return(EOF);
578 
579 	for (p=line;p && *p;) {
580 		p = next_token (p, &pair);
581 
582 		for (i=0; i<NUMFLDS; i++) {
583 			if (EQUALS(pair.name, _Kwords[i].kword)) {
584 				flds[i] = pair.value;
585 				break;
586 			}
587 		}
588 #ifndef UUCHECK
589 		ASSERT(i<NUMFLDS, "PERMISSIONS file: BAD OPTION--",
590 		    pair.name, NUMFLDS);
591 #else /* UUCHECK */
592 		if (i >= NUMFLDS) {
593 			DEBUG(3, "bad option (%s) in PERMISSIONS\n",pair.name);
594 			(void) printf("\n*****************************\n");
595 			(void) printf(gettext("**BAD OPTION in PERMISSIONS file: %s\n"),
596 				pair.name);
597 			(void) printf("*****************************\n");
598 			Uerrors++;
599 			return(0);
600 		}
601 #endif /* UUCHECK */
602 
603 	}
604 	return(0);
605 }
606 
607 /*
608  * return a name value pair
609  *	string	-> input pointer
610  *	pair	-> name value pair
611  * return:
612  *	pointer to next character
613  */
614 char *
615 next_token (string, pair)
616 char *string;
617 struct name_value *pair;
618 {
619 	char	*prev = _uu_setlocale(LC_ALL, "C");
620 
621 	while ( (*string) && ((*string == '\t') || (*string == ' ')) )
622 		string++;
623 
624 	pair->name = string;
625 	while ((*string) && (*string != '='))
626 		string++;
627 	if (*string)
628 		*string++ = NULLCHAR;
629 
630 	pair->value = string;
631 	while ((*string) && (*string != '\t') && (*string != ' ')
632 	    && (*string != '\n'))
633 		string++;
634 
635 	if (*string)
636 		*string++ = NULLCHAR;
637 
638 	(void) _uu_resetlocale(LC_ALL, prev);
639 	return (string);
640 }
641 
642 /*
643  * get a line from the PERMISSIONS
644  * take care of comments (#) in col 1
645  * and continuations (\) in last col
646  * return:
647  *	len of line
648  *	0 -> end of file
649  */
650 int
651 getuline(fp, line)
652 FILE *fp;
653 char *line;
654 {
655 	char *p, *c;
656 	char buf[BUFSIZ];
657 
658 	p = line;
659 	for (;fgets(buf, BUFSIZ, fp) != NULL;) {
660 		/* remove trailing white space */
661 		c = &buf[strlen(buf)-1];
662 		while (c>=buf && (*c == '\n' || *c == '\t' || *c == ' ') )
663 			*c-- = NULLCHAR;
664 
665 		if (buf[0] == '#' || buf[0] == '\n' || buf[0] == NULLCHAR)
666 			continue;
667 		(void) strcpy(p, buf);
668 		p += strlen(buf);
669 		if ( *(p-1) == '\\')
670 			p--;
671 		else
672 			break;
673 	}
674 
675 	return(p-line);
676 }
677 
678 
679 #define SMAX	15
680 
681 /*
682  * get the next colon separated argument from the list
683  * return:
684  *	p -> pointer to next arg in string
685  * input:
686  *	str -> pointer to input string
687  * output:
688  *	name -> pointer to arg string
689  */
690 char *
691 nextarg(str, name)
692 char *str, **name;
693 {
694 	char *p, *b;
695 	static char buf[SMAX+1];
696 
697 	for(b=buf,p=str; *p != ':' && *p && b < buf+SMAX;)
698 		*b++ = *p++;
699 	*b++ = NULLCHAR;
700 	if (*p == ':')
701 		p++;
702 	*name = buf;
703 	return(p);
704 }
705 
706 /*
707  * check if requesting files is permitted
708  * return
709  *	TRUE -> request permitted
710  *	FALSE -> request denied
711  */
712 int
713 requestOK()
714 {
715 	return(_Request);
716 }
717 
718 /*
719  * myName - return my name from PERMISSIONS file
720  *	or if not there, from  uucpname()
721  * return: none
722  */
723 
724 void
725 myName(name)
726 char *name;
727 {
728 	if (*_MyName)
729 		strcpy(name, _MyName);
730 	else
731 		uucpname(name);
732 	return;
733 }
734 
735 /*
736  * check for callback required for any transaction
737  * return:
738  *	TRUE -> callback required
739  *	FALSE-> callback NOT required
740  */
741 int
742 callBack()
743 {
744 	return(_CallBack);
745 }
746 
747 /*
748  * check for callback to send any files from here
749  * This means that the called (SLAVE) system will not switch roles.
750  * return:
751  *	TRUE -> callback requried to send files
752  *	FALSE-> callback NOT required to send files
753  */
754 int
755 switchRole()
756 {
757 	return(_Switch);
758 }
759 
760 /*
761  * Check to see if command is valid for a specific machine.
762  * The PERMISSIONS file has an option COMMANDS=name1:name2:... for
763  * any machine that does not have the default list which is
764  * rmail
765  * Note that the PERMISSIONS file is read once for each system
766  * at the time the Rmtname is set in xprocess().
767  * Return codes:
768  *	ok: TRUE
769  *	fail: FALSE
770  */
771 int
772 cmdOK(cmd, fullcmd)
773 char	*cmd, *fullcmd;
774 {
775 	DEBUG(7, "cmdOK(%s, )\n", cmd);
776 	return(cmdMatch(cmd, fullcmd));
777 }
778 
779 
780 /*
781  * check a name against a list
782  * input:
783  *	name	-> name
784  *	list	-> list of names
785  * return:
786  *	TRUE	-> found path
787  *	FALSE	-> not found
788  */
789 static int
790 listMatch(name, list)
791 char *name, *list[];
792 {
793     int i;
794     char *temp, *tend;
795     struct stat statbuf;
796     dev_t _dev[MAXPATHS+1];
797     ino_t _ino[MAXPATHS+1];
798 
799     /* ino set to 0 so stat is only done first time */
800     for (i=0; list[i] != NULL; i++)
801 	_ino[i] = 0;
802 
803     /* try to match inodes */
804     if ( (temp = strdup(name)) != NULL ) {
805 	for ( tend = temp + strlen(temp) ; *temp; ) {
806 	    if ( stat(temp, &statbuf) == 0 ) {
807 		for (i=0; list[i] != NULL; i++) {
808 		    if ( _ino[i] == 0 ) {
809 			struct stat tempbuf;
810 			if ( stat(list[i], &tempbuf) == 0 ) {
811 			    _dev[i] = tempbuf.st_dev;
812 			    _ino[i] = tempbuf.st_ino;
813 			}
814 		    }
815 		    if ( _dev[i] == statbuf.st_dev
816 		      && _ino[i] == statbuf.st_ino ) {
817 			free(temp);
818 			return(TRUE);
819 		    }
820 		}
821 	    }
822 	    *tend = '\0';
823 	    if ( (tend = strrchr(temp, '/')) == NULL ) {
824 		free(temp);
825 		break;
826 	    } else
827 		*(tend+1) = '\0';
828 	}
829     }
830 
831     return(FALSE);
832 }
833 
834 
835 /*
836  * Check "name" against a BASENAME or full name of _Commands list.
837  * If "name" specifies full path, check full, else check BASENAME.
838  *  e.g. "name" rmail matches list item /usr/bin/rmail
839  * input:
840  *	name	-> name
841  * output:
842  *	fullname -> copy full command name into fullname if
843  *		    a full path was specified in _Commands;
844  *		    if not, put name into fullname.
845  * return:
846  *	TRUE	-> found path
847  *	FALSE	-> not found
848  */
849 static int
850 cmdMatch(name, fullname)
851 char *name;
852 char *fullname;
853 {
854 	int i;
855 	char *bname;
856 	int allok = FALSE;
857 
858 	for (i=0; _Commands[i] != NULL; i++) {
859 		if (EQUALS(_Commands[i], "ALL")) {
860 			/* if ALL specified in the list
861 			 * set allok and continue in case
862 			 * a full path name is specified for the command
863 			 */
864 			allok = TRUE;
865 			continue;
866 		}
867 		if (name[0] != '/')
868 			bname = BASENAME(_Commands[i], '/');
869 		else
870 			bname = _Commands[i];
871 		DEBUG(7, "bname=%s\n", bname);
872 		if (EQUALS(bname, name)) {
873 			(void) strcpy(fullname, _Commands[i]);
874 			return(TRUE);
875 		}
876 	}
877 	if (allok == TRUE) {
878 		/* ALL was specified and the command was not found in list */
879 		(void) strcpy(fullname, name);
880 		return(TRUE);
881 	}
882 	(void) strcpy(fullname, "NuLL");	/* this is a dummy command */
883 	return(FALSE);
884 }
885 
886 
887 /*
888  * check the paths for this login/machine
889  * input:
890  *	path	pathname
891  *	flag	CK_READ or CK_WRITE
892  * output:
893  *	path	may be modified to canonical form
894  *		(../, ./, // will be interpreted/removed)
895  * returns:
896  *	0		-> success
897  *	FAIL		-> failure - not a valid path for access
898  */
899 int
900 chkpth(path, flag)
901 char *path;
902 {
903 	char *s;
904 
905 	/*
906 	 * this is probably redundant,
907 	 * because expfile did it, but that's ok
908 	 * Note - the /../ check is not required because of canPath
909 	 */
910 	if (canPath(path) == FAIL)
911 		return(FAIL);
912 
913 	if (flag == CK_READ)
914 		if (listMatch(path, _RPaths)
915 		&& !listMatch(path, _NoRPaths))
916 			return(0);
917 	if (flag == CK_WRITE)
918 		if (listMatch(path, _WPaths)
919 		&& !listMatch(path, _NoWPaths))
920 			return(0);
921 
922 
923 	/* ok if uucp generated D. or X. name for the spool directory */
924 	if (PREFIX(RemSpool, path) ) {
925     		s = &path[strlen(RemSpool)];
926 		if ( (*s++ == '/')
927 		  && (*s == DATAPRE || *s == XQTPRE)
928 		  && (*(++s) == '.')
929 		  && (strchr(s, '/') == NULL) )
930 			return(0);
931 	}
932 
933 	/*  path name not valid */
934 	return(FAIL);
935 }
936 
937 /*
938  * check write permission of file.
939  * if mopt != NULL and permissions are ok,
940  * a side effect of this routine is to make
941  * directories up to the last part of the
942  * "to" ( if they do not exit).
943  * Input:
944  *	to - a path name of the destination file or directory
945  *	from - full path name of source file
946  *	opt - create directory option (NULL - don't create)
947  * Output:
948  *	to - will be the full path name of the destination file
949  * returns:
950  *	0	->success
951  *	FAIL	-> failure
952  */
953 int
954 chkperm(from, to, opt)
955 char *from, *to, *opt;
956 {
957 	char *lxp, *p;
958 	struct stat s;
959 	char dir[MAXFULLNAME];
960 
961 	if (*(p = LASTCHAR(to)) == '/') {
962 	    if (strlcpy(p+1, BASENAME(from, '/'), MAXFULLNAME - strlen(to)) >=
963 		MAXFULLNAME - strlen(to)) {
964 		    return(FAIL);
965 	    }
966 	} else if (DIRECTORY(to)) {
967 	    *++p = '/';
968 	    if (strlcpy(p+1, BASENAME(from, '/'), MAXFULLNAME - strlen(to)) >=
969 		MAXFULLNAME - strlen(to)) {
970 		    return(FAIL);
971 	    }
972 	}
973 
974 	/* to is now the full path name of the destination file */
975 
976 	if (WRITEANY(to))
977 	    return(0);
978 	if (stat(to, &s) == 0)
979 	    return(FAIL);	/* file exists, but not writeable */
980 
981 	/* file does not exist--check directory and make when necessary */
982 
983 	(void) strcpy(dir, to);
984 	if ( (lxp=strrchr(dir, '/')) == NULL)
985 	    return(FAIL);	/* no directory part of name */
986 	if (lxp == dir)	/* at root */
987 	    lxp++;
988 	*lxp = NULLCHAR;
989 
990 	/* should check WRITEANY on parent before mkdirs() */
991 	if (!DIRECTORY(dir)) {
992 	    if (opt == NULL)
993 		return(FAIL);	/* no directory and no opt to make them */
994 	    else if (mkdirs(dir, PUBMASK) == FAIL)
995 		return(FAIL);
996 	}
997 
998 	/* the directory now exists--check for writability */
999 	if (EQUALS(RemSpool, dir) || WRITEANY(dir))
1000 	    return(0);
1001 
1002 	return(FAIL);
1003 }
1004