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