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