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  * A copy of the CDDL is also available via the Internet at
11  * http://www.opensource.org/licenses/cddl1.txt
12  * See the License for the specific language governing permissions
13  * and limitations under the License.
14  *
15  * When distributing Covered Code, include this CDDL HEADER in each
16  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
17  * If applicable, add the following below this CDDL HEADER, with the
18  * fields enclosed by brackets "[]" replaced with your own identifying
19  * information: Portions Copyright [yyyy] [name of copyright owner]
20  *
21  * CDDL HEADER END
22  */
23 
24 /*
25  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
26  * Use is subject to license terms.
27  */
28 
29 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
30 /*	  All Rights Reserved  	*/
31 
32 #if defined(sun)
33 #pragma ident	"@(#)hashserv.c	1.14	06/06/16 SMI"
34 #endif
35 
36 #include "defs.h"
37 
38 /*
39  * Copyright 2008-2019 J. Schilling
40  *
41  * @(#)hashserv.c	1.38 19/01/09 2008-2019 J. Schilling
42  */
43 #ifndef lint
44 static	UConst char sccsid[] =
45 	"@(#)hashserv.c	1.38 19/01/09 2008-2019 J. Schilling";
46 #endif
47 
48 /*
49  *	UNIX shell
50  */
51 
52 #include	"hash.h"
53 #include	<sys/types.h>
54 #include	<sys/stat.h>
55 #include	<errno.h>
56 #include	<schily/fcntl.h>	/* for faccessat() */
57 
58 static unsigned char	cost;
59 static int	dotpath;		/* PATH index for "::" */
60 static int	multrel;		/* Multiple "::" entries exist */
61 static struct entry	relcmd;		/* List of "relative" cmds */
62 
63 	short	pathlook	__PR((unsigned char *com,
64 					int flg, struct argnod *arg));
65 static void	zapentry	__PR((ENTRY *h));
66 	void	zaphash		__PR((void));
67 	void	zapcd		__PR((void));
68 static void	hashout		__PR((ENTRY *h));
69 	void	hashpr		__PR((void));
70 	void	set_dotpath	__PR((void));
71 	void	hash_func	__PR((unsigned char *name));
72 	void	func_unhash	__PR((unsigned char *name));
73 	short	hash_cmd	__PR((unsigned char *name));
74 	int	what_is_path	__PR((unsigned char *name, int verbose));
75 static	int	findpath	__PR((unsigned char *name, int oldpath));
76 	int	chk_access	__PR((unsigned char *name,
77 					mode_t mode, int regflag));
78 static void	pr_path		__PR((unsigned char *name, int count));
79 static int	argpath		__PR((struct argnod *arg));
80 
81 /*
82  * The central PATH and builtin / function search routine.
83  */
84 short
pathlook(com,flg,arg)85 pathlook(com, flg, arg)
86 	unsigned char	*com;	/* The command name to look for */
87 	int		flg;	/* Whether to manage cache hits */
88 	struct argnod	*arg;	/* Additional environment for cmd */
89 {
90 	unsigned char	*name = com;
91 	ENTRY		*h;
92 
93 	ENTRY		hentry;
94 	const struct sysnod	*sn;
95 #ifdef	HAVE_LOADABLE_LIBS
96 	const struct sysnod2	*sn2;
97 #endif
98 	int		count = 0;
99 	int		i;
100 	int		pathset = 0;
101 	int		oldpath = 0;
102 
103 	hentry.data = 0;
104 
105 	if (any('/', name))
106 		return (COMMAND);
107 
108 	h = hfind(name);
109 	if (h && (flags & ppath)) {
110 		if (h->data & (BUILTIN | FUNCTION))
111 			if (!(h->data & FUNCTION) || !(flags & nofuncs))
112 				return (h->data);
113 		/*
114 		 * Do not hash regular commands with "command -p ..."
115 		 */
116 		h = NULL;
117 	}
118 
119 	if (h) {
120 		if (h->data & (BUILTIN | FUNCTION)) {
121 			if (flg)
122 				h->hits++;
123 			if (!(h->data & FUNCTION) || !(flags & nofuncs))
124 				return (h->data);
125 		}
126 
127 		if (arg && (pathset = argpath(arg)))
128 			return (PATH_COMMAND);
129 
130 		if ((h->data & DOT_COMMAND) == DOT_COMMAND) {
131 			if (multrel == 0 && hashdata(h->data) > dotpath)
132 				oldpath = hashdata(h->data);
133 			else
134 				oldpath = dotpath;
135 
136 			h->data = 0;
137 			goto pathsrch;
138 		}
139 
140 		if (h->data & (COMMAND | REL_COMMAND)) {
141 			if (flg)
142 				h->hits++;
143 			return (h->data);
144 		}
145 
146 		/*
147 		 * If we are called from "command", do not modify the hashes
148 		 * as we may retrieve different results from what we usually
149 		 * expexct.
150 		 */
151 		if (!(flags & nofuncs)) {
152 			h->data = 0;
153 			h->cost = 0;
154 		}
155 	}
156 
157 #ifdef	HAVE_LOADABLE_LIBS
158 	if ((sn2 = sh_findbuiltin(name)) != NULL) {
159 		i = SYSLOADABLE;
160 		hentry.data = (BUILTIN | i);
161 		if ((flags & (ppath|nofuncs)))
162 			return (hentry.data);
163 		count = 1;
164 	} else
165 #endif
166 	if ((sn = sysnlook(name, commands, no_commands)) != NULL) {
167 		i = sn->sysval;
168 		if (sn->sysflg & BLT_SPC)
169 			i |= SPC_BUILTIN;
170 		hentry.data = (BUILTIN | i);
171 		/*
172 		 * If we are called from "command", do not modify the hashes
173 		 * as we may retrieve different results from what we usually
174 		 * expexct.
175 		 */
176 		if ((flags & (ppath|nofuncs)))
177 			return (hentry.data);
178 		count = 1;
179 	} else {
180 		if (arg && (pathset = argpath(arg)))
181 			return (PATH_COMMAND);
182 pathsrch:
183 		count = findpath(name, oldpath);
184 		/*
185 		 * If we are called from "command", do not modify the hashes
186 		 * as we may retrieve different results from what we usually
187 		 * expexct.
188 		 */
189 		if ((flags & (ppath|nofuncs))) {
190 			if (count > 0)
191 				return (COMMAND | count);
192 			else
193 				return (-count);
194 		}
195 	}
196 
197 	if (count > 0) {
198 		if (h == 0) {
199 			hentry.cost = 0;
200 			hentry.key = make(name);
201 			h = henter(hentry);
202 		}
203 
204 		if (h->data == 0) {
205 			if (count < dotpath) {
206 				h->data = COMMAND | count;
207 			} else {
208 				h->data = REL_COMMAND | count;
209 				h->next = relcmd.next;
210 				relcmd.next = h;
211 			}
212 		}
213 
214 		h->hits = flg;
215 		h->cost += cost;
216 		return (h->data);
217 	} else {
218 		return (-count);
219 	}
220 }
221 
222 /*
223  * Remove any PATH related attributes from entry.
224  */
225 static void
zapentry(h)226 zapentry(h)
227 	ENTRY *h;
228 {
229 	h->data &= HASHZAP;
230 }
231 
232 /*
233  * Remove any PATH related attributes from whole hash database.
234  */
235 void
zaphash()236 zaphash()
237 {
238 	hscan(zapentry);
239 	relcmd.next = 0;
240 }
241 
242 /*
243  * Mark all relative commands as outdated after a chdir()
244  */
245 void
zapcd()246 zapcd()
247 {
248 	ENTRY *ptr = relcmd.next;
249 
250 	while (ptr) {
251 		ptr->data |= CDMARK;
252 		ptr = ptr->next;
253 	}
254 	relcmd.next = 0;
255 }
256 
257 /*
258  * Print hash attributes for entry.
259  */
260 static void
hashout(h)261 hashout(h)
262 	ENTRY *h;
263 {
264 	sigchk();
265 
266 	if (hashtype(h->data) == NOTFOUND)
267 		return;
268 
269 	if (h->data & (BUILTIN | FUNCTION))
270 		return;
271 
272 	prn_buff(h->hits);
273 
274 	if (h->data & REL_COMMAND)
275 		prc_buff('*');
276 
277 	prc_buff(TAB);
278 	prn_buff(h->cost);
279 	prc_buff(TAB);
280 
281 	pr_path(h->key, hashdata(h->data));
282 	prc_buff(NL);
283 }
284 
285 /*
286  * Print hash statistics.
287  */
288 void
hashpr()289 hashpr()
290 {
291 	prs_buff(_gettext("hits	cost	command\n"));
292 	hscan(hashout);
293 }
294 
295 /*
296  * Find index of "::" in PATH and remember the result in 'dotpath'.
297  */
298 void
set_dotpath()299 set_dotpath()
300 {
301 	unsigned char	*path;
302 	int		cnt = 1;
303 
304 	dotpath = 10000;
305 	path = getpath((unsigned char *)"");
306 
307 	while (path && *path) {
308 		if (*path == '/') {
309 			cnt++;
310 		} else {
311 			if (dotpath == 10000) {
312 				dotpath = cnt;
313 			} else {
314 				multrel = 1;
315 				return;
316 			}
317 		}
318 		path = nextpath(path);
319 	}
320 	multrel = 0;
321 }
322 
323 /*
324  * Add new function to hash.
325  */
326 void
hash_func(name)327 hash_func(name)
328 	unsigned char	*name;
329 {
330 	ENTRY	*h;
331 	ENTRY	hentry;
332 
333 	h = hfind(name);
334 
335 	if (h) {
336 		h->data = FUNCTION;
337 	} else {
338 		hentry.data = FUNCTION;
339 		hentry.key = make(name);
340 		hentry.cost = 0;
341 		hentry.hits = 0;
342 		hentry.next = NULL;
343 		henter(hentry);
344 	}
345 }
346 
347 /*
348  * Remove function from hash.
349  * Reestablish builtin if function did hide the builtin.
350  */
351 void
func_unhash(name)352 func_unhash(name)
353 	unsigned char	*name;
354 {
355 	const struct sysnod	*sn;
356 #ifdef	HAVE_LOADABLE_LIBS
357 	const struct sysnod2	*sn2;
358 #endif
359 	ENTRY			*h;
360 
361 	h = hfind(name);
362 
363 	if (h && (h->data & FUNCTION)) {
364 
365 #ifdef	HAVE_LOADABLE_LIBS
366 		if ((sn2 = sh_findbuiltin(name)) != NULL) {
367 			int	i;
368 
369 			i = SYSLOADABLE;
370 			h->data = (BUILTIN | i);
371 		} else
372 #endif
373 		if ((sn = sysnlook(name, commands, no_commands)) != NULL) {
374 			int	i;
375 
376 			i = sn->sysval;
377 			if (sn->sysflg & BLT_SPC)
378 				i |= SPC_BUILTIN;
379 			h->data = (BUILTIN | i);
380 		} else {
381 			h->data = NOTFOUND;
382 		}
383 	}
384 }
385 
386 /*
387  * Hash search / entry code for "hash" builtin.
388  */
389 short
hash_cmd(name)390 hash_cmd(name)
391 	unsigned char *name;
392 {
393 	ENTRY	*h;
394 
395 	if (any('/', name))
396 		return (COMMAND);
397 
398 	h = hfind(name);
399 
400 	if (h) {
401 		if (h->data & (BUILTIN | FUNCTION)) {
402 			return (h->data);
403 		} else if ((h->data & REL_COMMAND) == REL_COMMAND) {
404 			/*
405 			 * unlink h from relative command list
406 			 */
407 			ENTRY *ptr = &relcmd;
408 			while (ptr-> next != h)
409 				ptr = ptr->next;
410 			ptr->next = h->next;
411 		}
412 		zapentry(h);
413 	}
414 
415 	return (pathlook(name, 0, (struct argnod *)0));
416 }
417 
418 
419 /*
420  * Search command an print command type.
421  * Return 0 if found, 1 if not.
422  */
423 int
what_is_path(name,verbose)424 what_is_path(name, verbose)
425 	unsigned char	*name;
426 	int		verbose;
427 {
428 	ENTRY	*h;
429 	int	cnt;
430 	int	amt;
431 	short	hashval;
432 	const struct sysnod	*sp;
433 #ifdef	HAVE_LOADABLE_LIBS
434 	const struct sysnod2	*sp2;
435 	struct sysnod		sn;
436 #endif
437 
438 	h = hfind(name);
439 	if (h && (flags & ppath)) {
440 		/*
441 		 * Do not hash regular commands with "command -p ..."
442 		 */
443 		if (!(h->data & (BUILTIN | FUNCTION)))
444 			h = NULL;
445 	}
446 
447 	amt = prs_buff(name);
448 	if (h) {
449 		hashval = hashdata(h->data);
450 
451 		switch (hashtype(h->data)) {
452 
453 		case BUILTIN:
454 			if (!verbose) {
455 				prc_buff(NL);
456 				return (0);
457 			}
458 #ifdef DO_POSIX_TYPE
459 			/*
460 			 * In POSIX mode, we distinct three different types of
461 			 * builtins and thus need to check explicitly.
462 			 */
463 			goto checkbuiltin;
464 #else
465 			prs_buff(_gettext(" is a shell builtin\n"));
466 			return (0);
467 #endif
468 
469 		case FUNCTION: {
470 			struct namnod *n = lookup(name);
471 			struct fndnod *f = fndptr(n->funcval);
472 
473 			if (!verbose) {
474 				prc_buff(NL);
475 				return (0);
476 			}
477 			prs_buff(_gettext(" is a function\n"));
478 			if (verbose <= 1)
479 				return (0);
480 			prs_buff(name);
481 			prs_buff((unsigned char *)"(){\n");
482 			if (f != NULL)
483 				prf((struct trenod *)f->fndval);
484 			prs_buff((unsigned char *)"\n}\n");
485 			return (0);
486 		}
487 
488 		case REL_COMMAND: {
489 			short	hash;
490 
491 			if ((h->data & DOT_COMMAND) == DOT_COMMAND) {
492 				hash = pathlook(name, 0, (struct argnod *)0);
493 				if (hashtype(hash) == NOTFOUND) {
494 					if (!verbose) {
495 						unprs_buff(amt);
496 						return (ERR_NOTFOUND);
497 					}
498 					prs_buff(_gettext(" not found\n"));
499 					return (ERR_NOTFOUND);
500 				} else {
501 					hashval = hashdata(hash);
502 				}
503 			}
504 		}
505 		/* FALLTHROUGH */
506 
507 		case COMMAND:
508 			if (!verbose) {
509 				unprs_buff(amt);
510 				pr_path(name, hashval);
511 				prc_buff(NL);
512 				return (0);
513 			}
514 			prs_buff(_gettext(" is hashed ("));
515 			pr_path(name, hashval);
516 			prs_buff((unsigned char *)")\n");
517 			return (0);
518 		}
519 	}
520 
521 #ifdef DO_POSIX_TYPE
522 	if (syslook(name, reserved, no_reserved)) {
523 		if (!verbose) {
524 			prc_buff(NL);
525 			return (0);
526 		}
527 		prs_buff(_gettext(" is a keyword\n"));
528 		return (0);
529 	}
530 
531 checkbuiltin:
532 #endif
533 
534 #if	defined(HAVE_LOADABLE_LIBS) && defined(DO_SYSBUILTIN)
535 	if ((sp2 = sh_findbuiltin(name)) != NULL) {
536 		sn.sysflg = 0;
537 		sp = &sn;
538 		goto isbltin;
539 	} else
540 #endif
541 	if ((sp = sysnlook(name, commands, no_commands)) != NULL) {
542 #if	defined(HAVE_LOADABLE_LIBS) && defined(DO_SYSBUILTIN)
543 	isbltin:
544 #endif
545 		if (!verbose) {
546 			prc_buff(NL);
547 			return (0);
548 		}
549 #ifdef DO_POSIX_TYPE
550 		if (sp->sysflg & BLT_SPC)
551 			prs_buff(_gettext(" is a special shell builtin\n"));
552 		else if (sp->sysflg & BLT_INT)
553 			prs_buff(_gettext(" is a shell intrinsic\n"));
554 		else
555 #endif
556 			prs_buff(_gettext(" is a shell builtin\n"));
557 		return (0);
558 	}
559 
560 	if ((cnt = findpath(name, 0)) > 0) {
561 		if (!verbose) {
562 			unprs_buff(amt);
563 			pr_path(name, cnt);
564 			prc_buff(NL);
565 			return (0);
566 		}
567 		prs_buff(_gettext(" is "));
568 		pr_path(name, cnt);
569 		prc_buff(NL);
570 		return (0);
571 	} else {
572 		if (!verbose) {
573 			unprs_buff(amt);
574 			return (ERR_NOTFOUND);
575 		}
576 		prs_buff(_gettext(" not found\n"));
577 		return (ERR_NOTFOUND);
578 	}
579 }
580 
581 /*
582  * Find 'name' in PATH and return index.
583  * Start search at 'oldpath'.
584  */
585 static int
findpath(name,oldpath)586 findpath(name, oldpath)
587 	unsigned char	*name;
588 	int		oldpath;
589 {
590 	unsigned char	*path;
591 	int	count = 1;
592 
593 	unsigned char	*p;
594 	int	ok = 1;
595 	int	e_code = 1;
596 
597 	cost = 0;
598 	path = getpath(name);
599 
600 	if (oldpath) {
601 		count = dotpath;
602 		while (--count && path)
603 			path = nextpath(path);
604 
605 		if (oldpath > dotpath && path) {
606 			catpath(path, name);
607 			p = curstak();
608 			cost = 1;
609 
610 			if ((ok = chk_access(p, S_IEXEC, 1)) == 0)
611 				return (dotpath);
612 			else
613 				return (oldpath);
614 		} else {
615 			count = dotpath;
616 		}
617 	}
618 
619 	while (path) {
620 		path = catpath(path, name);
621 		cost++;
622 		p = curstak();
623 
624 		if ((ok = chk_access(p, S_IEXEC, 1)) == 0)
625 			break;
626 		else
627 			e_code = max(e_code, ok);
628 
629 		count++;
630 	}
631 
632 	return (ok ? -e_code : count);
633 }
634 
635 /*
636  * Determine if file given by name is accessible with permissions
637  * given by mode.
638  * Regflag argument non-zero means not to consider
639  * a non-regular file as executable.
640  *
641  * Return codes:
642  * 0	Access OK
643  * 1	Other error (e.g. file not found)
644  * 2	Executable but not regular
645  * 3	File found but access error
646  */
647 #ifdef	PROTOTYPES
648 int
chk_access(unsigned char * name,mode_t mode,int regflag)649 chk_access(unsigned char *name, mode_t mode, int regflag)
650 #else
651 int
652 chk_access(name, mode, regflag)
653 	unsigned char	*name;		/* The filename */
654 	mode_t		mode;		/* F_OK, S_IREAD, S_IWRITE, S_IEXEC */
655 	int		regflag;	/* if TRUE: file must be regular */
656 #endif
657 {
658 	static int flag;
659 	static uid_t euid;
660 	struct stat statb;
661 	mode_t ftype;
662 
663 	if (flag == 0) {
664 		euid = geteuid();
665 		flag = 1;
666 	}
667 	if (stat((char *)name, &statb) == 0) {
668 		int	amode = 0;
669 
670 		ftype = statb.st_mode & S_IFMT;
671 		if (mode == S_IEXEC && regflag && ftype != S_IFREG)
672 			return (2);
673 
674 		if (mode == F_OK)
675 			amode = F_OK;
676 		if (mode & S_IREAD)
677 			amode |= R_OK;
678 		if (mode & S_IWRITE)
679 			amode |= W_OK;
680 		if (mode & S_IEXEC)
681 			amode |= X_OK;
682 
683 #ifdef	HAVE_FACCESSAT
684 		if (faccessat(AT_FDCWD, (char *)name, amode, AT_EACCESS) == 0) {
685 #else
686 #ifdef	HAVE_ACCESS_E_OK
687 		if (access((char *)name, 010|amode) == 0) {
688 #else
689 		if (access((char *)name, amode) == 0) {
690 #endif
691 #endif
692 			if (euid == 0) {
693 				if (ftype != S_IFREG || mode != S_IEXEC)
694 					return (0);
695 				/*
696 				 * root can execute file as long as it has
697 				 * execute permission for someone
698 				 */
699 				if (statb.st_mode &
700 				    (S_IEXEC|(S_IEXEC>>3)|(S_IEXEC>>6))) {
701 					return (0);
702 				}
703 				return (3);
704 			}
705 			return (0);
706 		}
707 	}
708 	return (errno == EACCES ? 3 : 1);
709 }
710 
711 /*
712  * Print path for command based on command name and index in PATH
713  */
714 static void
pr_path(name,count)715 pr_path(name, count)
716 	unsigned char	*name;
717 	int		count;
718 {
719 	unsigned char	*path;
720 
721 	path = getpath(name);
722 
723 	while (--count && path)
724 		path = nextpath(path);
725 
726 	if (path == NULL)	/* Paranoia for Coverity */
727 		return;
728 
729 	catpath(path, name);
730 	prs_buff(curstak());
731 }
732 
733 /*
734  * Return 1 if argnode contains a PATH= definition.
735  */
736 static int
argpath(arg)737 argpath(arg)
738 	struct argnod	*arg;
739 {
740 	unsigned char	*s;
741 	unsigned char	*start;
742 
743 	while (arg) {
744 		s = arg->argval;
745 		start = s;
746 
747 		if (letter(*s)) {
748 			while (alphanum(*s))
749 				s++;
750 
751 			if (*s == '=') {
752 				*s = 0;
753 
754 				if (eq(start, pathname)) {
755 					*s = '=';
756 					return (1);
757 				} else {
758 					*s = '=';
759 				}
760 			}
761 		}
762 		arg = arg->argnxt;
763 	}
764 
765 	return (0);
766 }
767