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 */
61static char *_Flds[NUMFLDS];
62
63/* keyword/value structure */
64struct keywords {
65	char* kword;
66	int kvalue;
67};
68static 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 */
92static char *_RPaths[MAXPATHS+1];
93static char *_WPaths[MAXPATHS+1];
94static char *_NoRPaths[MAXPATHS+1];
95static char *_NoWPaths[MAXPATHS+1];
96static char *_Commands[MAXCMDS+1];
97static char _Cmd_defaults[BUFSIZ];
98
99/* option variables */
100static int _Request;	/* TRUE can request, FALSE can not request files */
101static int _Switch;	/* FALSE requires a call back to send any files */
102static int _CallBack;	/* TRUE for call back for any transaction */
103static int _NoSpool;	/* TRUE if delivering directly to destination file */
104static char _MyName[MAXBASENAME+1];	/* Myname from PERMISSIONS file */
105/* NB: _Pubdir and _Path assumed to point to dynamic data */
106static char *_Pubdir = NULL;		/* PUBDIR from PERMISSIONS file */
107static char *_Path = NULL;		/* PATH from PERMISSIONS file */
108
109struct name_value
110{
111	char *name;
112	char *value;
113};
114
115/* file pointer for PERMISSIONS */
116static FILE *Fp = NULL;
117
118/* functions */
119extern char *next_token(), *nextarg();
120extern int parse_tokens(), canPath(), mkdirs();
121static void fillFlds();
122static void fillList();
123static int cmdMatch(), listMatch(), nameMatch(),
124	userFind(), validateFind();
125
126int
127noSpool()
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
142int
143logFind(name, rmtname)
144char *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
180int
181mchFind(name)
182char *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 */
216static int
217nameMatch(name, fld)
218char *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 */
237static void
238fillFlds()
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 */
315static void
316fillList(type, list)
317int type;
318char *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 */
379static int
380userFind(name, rmtname, type)
381char *name, *rmtname;
382int 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 */
483static int
484validateFind(name)
485char *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 */
516char *
517aliasFind(name)
518char *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 */
562int
563parse_tokens(flds, buf)
564char *flds[];
565char *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 */
617char *
618next_token (string, pair)
619char *string;
620struct 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 */
653int
654getuline(fp, line)
655FILE *fp;
656char *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 */
693char *
694nextarg(str, name)
695char *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 */
715int
716requestOK()
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
727void
728myName(name)
729char *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 */
744int
745callBack()
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 */
757int
758switchRole()
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 */
774int
775cmdOK(cmd, fullcmd)
776char	*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 */
792static int
793listMatch(name, list)
794char *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 */
852static int
853cmdMatch(name, fullname)
854char *name;
855char *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 */
902int
903chkpth(path, flag)
904char *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 */
956int
957chkperm(from, to, opt)
958char *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