1/*	$OpenBSD: common.c,v 1.41 2022/12/26 19:16:02 jmc Exp $	*/
2
3/*
4 * Copyright (c) 1983 Regents of the University of California.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 *    may be used to endorse or promote products derived from this software
17 *    without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#include <sys/stat.h>
33#include <sys/time.h>
34#include <sys/wait.h>
35
36#include <errno.h>
37#include <fcntl.h>
38#include <grp.h>
39#include <limits.h>
40#include <paths.h>
41#include <stdarg.h>
42#include <stdio.h>
43#include <stdlib.h>
44#include <string.h>
45#include <unistd.h>
46
47#include "defs.h"
48
49/*
50 * Things common to both the client and server.
51 */
52
53/*
54 * Variables common to both client and server
55 */
56char			host[HOST_NAME_MAX+1];	/* Name of this host */
57uid_t			userid = (uid_t)-1;	/* User's UID */
58gid_t			groupid = (gid_t)-1;	/* User's GID */
59gid_t		        gidset[NGROUPS_MAX];	/* User's GID list */
60int			gidsetlen = 0;		/* Number of GIDS in list */
61char		       *homedir = NULL;		/* User's $HOME */
62char		       *locuser = NULL;		/* Local User's name */
63int			isserver = FALSE;	/* We're the server */
64int     		amchild = 0;		/* This PID is a child */
65int			do_fork = 1;		/* Fork child process */
66char		       *currenthost = NULL;	/* Current client hostname */
67char		       *progname = NULL;	/* Name of this program */
68int			rem_r = -1;		/* Client file descriptor */
69int			rem_w = -1;		/* Client file descriptor */
70volatile sig_atomic_t 	contimedout = FALSE;	/* Connection timed out */
71int			rtimeout = RTIMEOUT;	/* Response time out */
72jmp_buf			finish_jmpbuf;		/* Finish() jmp buffer */
73int			setjmp_ok = FALSE;	/* setjmp()/longjmp() status */
74char		      **realargv;		/* Real main() argv */
75int			realargc;		/* Real main() argc */
76opt_t			options = 0;		/* Global install options */
77char			defowner[64] = "bin";	/* Default owner */
78char			defgroup[64] = "bin";	/* Default group */
79
80static int sendcmdmsg(int, char *, size_t);
81static ssize_t remread(int, u_char *, size_t);
82static int remmore(void);
83
84/*
85 * Front end to write() that handles partial write() requests.
86 */
87ssize_t
88xwrite(int fd, void *buf, size_t len)
89{
90    	size_t nleft = len;
91	ssize_t nwritten;
92	char *ptr = buf;
93
94	while (nleft > 0) {
95	    	if ((nwritten = write(fd, ptr, nleft)) <= 0) {
96			return nwritten;
97	    	}
98	    	nleft -= nwritten;
99	    	ptr += nwritten;
100	}
101
102	return len;
103}
104
105/*
106 * Do run-time initialization
107 */
108int
109init(int argc, char **argv, char **envp)
110{
111	struct passwd *pw;
112	int i;
113
114	/*
115	 * Save a copy of our argc and argv before setargs() overwrites them
116	 */
117	realargc = argc;
118	realargv = xmalloc(sizeof(char *) * (argc+1));
119	for (i = 0; i < argc; i++)
120		realargv[i] = xstrdup(argv[i]);
121
122	pw = getpwuid(userid = getuid());
123	if (pw == NULL) {
124		error("Your user id (%u) is not known to this system.",
125		      getuid());
126		return(-1);
127	}
128
129	debugmsg(DM_MISC, "UserID = %u pwname = '%s' home = '%s'\n",
130		 userid, pw->pw_name, pw->pw_dir);
131	homedir = xstrdup(pw->pw_dir);
132	locuser = xstrdup(pw->pw_name);
133	groupid = pw->pw_gid;
134	gidsetlen = getgroups(NGROUPS_MAX, gidset);
135	gethostname(host, sizeof(host));
136#if 0
137	if ((cp = strchr(host, '.')) != NULL)
138	    	*cp = CNULL;
139#endif
140
141	/*
142	 * If we're not root, disable paranoid ownership checks
143	 * since normal users cannot chown() files.
144	 */
145	if (!isserver && userid != 0) {
146		FLAG_ON(options, DO_NOCHKOWNER);
147		FLAG_ON(options, DO_NOCHKGROUP);
148	}
149
150	return(0);
151}
152
153/*
154 * Finish things up before ending.
155 */
156void
157finish(void)
158{
159	debugmsg(DM_CALL,
160		 "finish() called: do_fork = %d amchild = %d isserver = %d",
161		 do_fork, amchild, isserver);
162	cleanup(0);
163
164	/*
165	 * There's no valid finish_jmpbuf for the rdist master parent.
166	 */
167	if (!do_fork || amchild || isserver) {
168
169		if (!setjmp_ok) {
170#ifdef DEBUG_SETJMP
171			error("attempting longjmp() without target");
172			abort();
173#else
174			exit(1);
175#endif
176		}
177
178		longjmp(finish_jmpbuf, 1);
179		/*NOTREACHED*/
180		error("Unexpected failure of longjmp() in finish()");
181		exit(2);
182	} else
183		exit(1);
184}
185
186/*
187 * Handle lost connections
188 */
189void
190lostconn(void)
191{
192	/* Prevent looping */
193	(void) signal(SIGPIPE, SIG_IGN);
194
195	rem_r = rem_w = -1;	/* Ensure we don't try to send to server */
196	checkhostname();
197	error("Lost connection to %s",
198	      (currenthost) ? currenthost : "(unknown)");
199
200	finish();
201}
202
203/*
204 * General signal handler
205 */
206void
207sighandler(int sig)
208{
209	int save_errno = errno;
210
211	/* XXX signal race */
212	debugmsg(DM_CALL, "sighandler() received signal %d\n", sig);
213
214	switch (sig) {
215	case SIGALRM:
216		contimedout = TRUE;
217		/* XXX signal race */
218		checkhostname();
219		error("Response time out");
220		finish();
221		break;
222
223	case SIGPIPE:
224		/* XXX signal race */
225		lostconn();
226		break;
227
228	case SIGFPE:
229		debug = !debug;
230		break;
231
232	case SIGHUP:
233	case SIGINT:
234	case SIGQUIT:
235	case SIGTERM:
236		/* XXX signal race */
237		finish();
238		break;
239
240	default:
241		/* XXX signal race */
242		fatalerr("No signal handler defined for signal %d.", sig);
243	}
244	errno = save_errno;
245}
246
247/*
248 * Function to actually send the command char and message to the
249 * remote host.
250 */
251static int
252sendcmdmsg(int cmd, char *msg, size_t msgsize)
253{
254	int len;
255
256	if (rem_w < 0)
257		return(-1);
258
259	/*
260	 * All commands except C_NONE should have a newline
261	 */
262	if (cmd != C_NONE && !strchr(msg + 1, '\n'))
263		(void) strlcat(msg + 1, "\n", msgsize - 1);
264
265	if (cmd == C_NONE)
266		len = strlen(msg);
267	else {
268		len = strlen(msg + 1) + 1;
269		msg[0] = cmd;
270	}
271
272	debugmsg(DM_PROTO, ">>> Cmd = %c (\\%3.3o) Msg = \"%.*s\"",
273		 cmd, cmd,
274		 (cmd == C_NONE) ? len-1 : len-2,
275		 (cmd == C_NONE) ? msg : msg + 1);
276
277	return(!(xwrite(rem_w, msg, len) == len));
278}
279
280/*
281 * Send a command message to the remote host.
282 * Called as sendcmd(char cmdchar, char *fmt, arg1, arg2, ...)
283 * The fmt may be NULL, in which case there are no args.
284 */
285int
286sendcmd(char cmd, const char *fmt, ...)
287{
288	static char buf[BUFSIZ];
289	va_list args;
290
291	va_start(args, fmt);
292	if (fmt)
293		(void) vsnprintf(buf + (cmd != C_NONE),
294				 sizeof(buf) - (cmd != C_NONE), fmt, args);
295	else
296		buf[1] = CNULL;
297	va_end(args);
298
299	return(sendcmdmsg(cmd, buf, sizeof(buf)));
300}
301
302/*
303 * Internal variables and routines for reading lines from the remote.
304 */
305static u_char rembuf[BUFSIZ];
306static u_char *remptr;
307static ssize_t remleft;
308
309#define remc() (--remleft < 0 ? remmore() : *remptr++)
310
311/*
312 * Back end to remote read()
313 */
314static ssize_t
315remread(int fd, u_char *buf, size_t bufsiz)
316{
317	return(read(fd, (char *)buf, bufsiz));
318}
319
320static int
321remmore(void)
322{
323	(void) signal(SIGALRM, sighandler);
324	(void) alarm(rtimeout);
325
326	remleft = remread(rem_r, rembuf, sizeof(rembuf));
327
328	(void) alarm(0);
329
330	if (remleft < 0)
331		return (-2);	/* error */
332	if (remleft == 0)
333		return (-1);	/* EOF */
334	remptr = rembuf;
335	remleft--;
336	return (*remptr++);
337}
338
339/*
340 * Read an input line from the remote.  Return the number of bytes
341 * stored (equivalent to strlen(p)).  If `cleanup' is set, EOF at
342 * the beginning of a line is returned as EOF (-1); other EOFs, or
343 * errors, call cleanup() or lostconn().  In other words, unless
344 * the third argument is nonzero, this routine never returns failure.
345 */
346int
347remline(u_char *buffer, int space, int doclean)
348{
349	int c, left = space;
350	u_char *p = buffer;
351
352	if (rem_r < 0) {
353		error("Cannot read remote input: Remote descriptor not open.");
354		return(-1);
355	}
356
357	while (left > 0) {
358		if ((c = remc()) < -1) {	/* error */
359			if (doclean) {
360				finish();
361				/*NOTREACHED*/
362			}
363			lostconn();
364			/*NOTREACHED*/
365		}
366		if (c == -1) {			/* got EOF */
367			if (doclean) {
368				if (left == space)
369					return (-1);/* signal proper EOF */
370				finish();	/* improper EOF */
371				/*NOTREACHED*/
372			}
373			lostconn();
374			/*NOTREACHED*/
375		}
376		if (c == '\n') {
377			*p = CNULL;
378
379			if (debug) {
380				static char mbuf[BUFSIZ];
381
382				(void) snprintf(mbuf, sizeof(mbuf),
383					"<<< Cmd = %c (\\%3.3o) Msg = \"%s\"",
384					       buffer[0], buffer[0],
385					       buffer + 1);
386
387				debugmsg(DM_PROTO, "%s", mbuf);
388			}
389
390			return (space - left);
391		}
392		*p++ = c;
393		left--;
394	}
395
396	/* this will probably blow the entire session */
397	error("remote input line too long");
398	p[-1] = CNULL;		/* truncate */
399	return (space);
400}
401
402/*
403 * Non-line-oriented remote read.
404 */
405ssize_t
406readrem(char *p, ssize_t space)
407{
408	if (remleft <= 0) {
409		/*
410		 * Set remote time out alarm.
411		 */
412		(void) signal(SIGALRM, sighandler);
413		(void) alarm(rtimeout);
414
415		remleft = remread(rem_r, rembuf, sizeof(rembuf));
416
417		(void) alarm(0);
418		remptr = rembuf;
419	}
420
421	if (remleft <= 0)
422		return (remleft);
423	if (remleft < space)
424		space = remleft;
425
426	memcpy(p, remptr, space);
427
428	remptr += space;
429	remleft -= space;
430
431	return (space);
432}
433
434/*
435 * Get the user name for the uid.
436 */
437char *
438getusername(uid_t uid, char *file, opt_t opts)
439{
440	static char buf[100];
441	static uid_t lastuid = (uid_t)-1;
442	const char *name;
443
444	/*
445	 * The value of opts may have changed so we always
446	 * do the opts check.
447	 */
448  	if (IS_ON(opts, DO_NUMCHKOWNER)) {
449		(void) snprintf(buf, sizeof(buf), ":%u", uid);
450		return(buf);
451  	}
452
453	/*
454	 * Try to avoid passwd lookup.
455	 */
456	if (lastuid == uid && buf[0] != '\0' && buf[0] != ':')
457		return(buf);
458
459	lastuid = uid;
460
461	if ((name = user_from_uid(uid, 1)) == NULL) {
462		if (IS_ON(opts, DO_DEFOWNER) && !isserver)
463			(void) strlcpy(buf, defowner, sizeof(buf));
464		else {
465			message(MT_WARNING,
466				"%s: No password entry for uid %u", file, uid);
467			(void) snprintf(buf, sizeof(buf), ":%u", uid);
468		}
469	} else {
470		(void) strlcpy(buf, name, sizeof(buf));
471	}
472
473	return(buf);
474}
475
476/*
477 * Get the group name for the gid.
478 */
479char *
480getgroupname(gid_t gid, char *file, opt_t opts)
481{
482	static char buf[100];
483	static gid_t lastgid = (gid_t)-1;
484	const char *name;
485
486	/*
487	 * The value of opts may have changed so we always
488	 * do the opts check.
489	 */
490  	if (IS_ON(opts, DO_NUMCHKGROUP)) {
491		(void) snprintf(buf, sizeof(buf), ":%u", gid);
492		return(buf);
493  	}
494
495	/*
496	 * Try to avoid group lookup.
497	 */
498	if (lastgid == gid && buf[0] != '\0' && buf[0] != ':')
499		return(buf);
500
501	lastgid = gid;
502
503	if ((name = group_from_gid(gid, 1)) == NULL) {
504		if (IS_ON(opts, DO_DEFGROUP) && !isserver)
505			(void) strlcpy(buf, defgroup, sizeof(buf));
506		else {
507			message(MT_WARNING, "%s: No name for group %u",
508				file, gid);
509			(void) snprintf(buf, sizeof(buf), ":%u", gid);
510		}
511	} else
512		(void) strlcpy(buf, name, sizeof(buf));
513
514	return(buf);
515}
516
517/*
518 * Read a response from the remote host.
519 */
520int
521response(void)
522{
523	static u_char resp[BUFSIZ];
524	u_char *s;
525	int n;
526
527	debugmsg(DM_CALL, "response() start\n");
528
529	n = remline(s = resp, sizeof(resp), 0);
530
531	n--;
532	switch (*s++) {
533        case C_ACK:
534		debugmsg(DM_PROTO, "received ACK\n");
535		return(0);
536	case C_LOGMSG:
537		if (n > 0) {
538			message(MT_CHANGE, "%s", s);
539			return(1);
540		}
541		debugmsg(DM_PROTO, "received EMPTY logmsg\n");
542		return(0);
543	case C_NOTEMSG:
544		if (s)
545			message(MT_NOTICE, "%s", s);
546		return(response());
547
548	default:
549		s--;
550		n++;
551		/* fall into... */
552
553	case C_ERRMSG:	/* Normal error message */
554		if (s)
555			message(MT_NERROR, "%s", s);
556		return(-1);
557
558	case C_FERRMSG:	/* Fatal error message */
559		if (s)
560			message(MT_FERROR, "%s", s);
561		finish();
562		return(-1);
563	}
564	/*NOTREACHED*/
565}
566
567/*
568 * This should be in expand.c but the other routines call other modules
569 * that we don't want to load in.
570 *
571 * Expand file names beginning with `~' into the
572 * user's home directory path name. Return a pointer in buf to the
573 * part corresponding to `file'.
574 */
575char *
576exptilde(char *ebuf, char *file, size_t ebufsize)
577{
578	struct passwd *pw;
579	char *pw_dir, *rest;
580	static char lastuser[_PW_NAME_LEN + 1];
581	static char lastdir[PATH_MAX];
582	size_t len;
583
584	if (*file != '~') {
585notilde:
586		(void) strlcpy(ebuf, file, ebufsize);
587		return(ebuf);
588	}
589	pw_dir = homedir;
590	if (*++file == CNULL) {
591		rest = NULL;
592	} else if (*file == '/') {
593		rest = file;
594	} else {
595		rest = file;
596		while (*rest && *rest != '/')
597			rest++;
598		if (*rest == '/')
599			*rest = CNULL;
600		else
601			rest = NULL;
602		if (strcmp(locuser, file) != 0) {
603			if (strcmp(lastuser, file) != 0) {
604				if ((pw = getpwnam(file)) == NULL) {
605					error("%s: unknown user name", file);
606					if (rest != NULL)
607						*rest = '/';
608					return(NULL);
609				}
610				strlcpy(lastuser, pw->pw_name, sizeof(lastuser));
611				strlcpy(lastdir, pw->pw_dir, sizeof(lastdir));
612			}
613			pw_dir = lastdir;
614		}
615		if (rest != NULL)
616			*rest = '/';
617	}
618	if ((len = strlcpy(ebuf, pw_dir, ebufsize)) >= ebufsize)
619		goto notilde;
620	pw_dir = ebuf + len;
621	if (rest != NULL) {
622		pw_dir++;
623		if ((len = strlcat(ebuf, rest, ebufsize)) >= ebufsize)
624			goto notilde;
625	}
626	return(pw_dir);
627}
628
629
630
631/*
632 * Set access and modify times of a given file
633 */
634int
635setfiletime(char *file, time_t atime, time_t mtime)
636{
637	struct timeval tv[2];
638
639	if (atime != 0 && mtime != 0) {
640		tv[0].tv_sec = atime;
641		tv[1].tv_sec = mtime;
642		tv[0].tv_usec = tv[1].tv_usec = 0;
643		return (utimes(file, tv));
644	} else	/* Set to current time */
645		return (utimes(file, NULL));
646}
647
648/*
649 * Get version info
650 */
651char *
652getversion(void)
653{
654	static char buff[BUFSIZ];
655
656	(void) snprintf(buff, sizeof(buff),
657	"Version %s.%d (%s) - Protocol Version %d, Release %s, Patch level %d",
658		       DISTVERSION, PATCHLEVEL, DISTSTATUS,
659		       VERSION, DISTVERSION, PATCHLEVEL);
660
661	return(buff);
662}
663
664/*
665 * Execute a shell command to handle special cases.
666 * This is now common to both server and client
667 */
668void
669runcommand(char *cmd)
670{
671	ssize_t nread;
672	pid_t pid, wpid;
673	char *cp, *s;
674	char sbuf[BUFSIZ], buf[BUFSIZ];
675	int fd[2], status;
676
677	if (pipe(fd) == -1) {
678		error("pipe of %s failed: %s", cmd, SYSERR);
679		return;
680	}
681
682	if ((pid = fork()) == 0) {
683		/*
684		 * Return everything the shell commands print.
685		 */
686		(void) close(0);
687		(void) close(1);
688		(void) close(2);
689		(void) open(_PATH_DEVNULL, O_RDONLY);
690		(void) dup(fd[PIPE_WRITE]);
691		(void) dup(fd[PIPE_WRITE]);
692		(void) close(fd[PIPE_READ]);
693		(void) close(fd[PIPE_WRITE]);
694		(void) execl(_PATH_BSHELL, "sh", "-c", cmd, (char *)NULL);
695		_exit(127);
696	}
697	(void) close(fd[PIPE_WRITE]);
698	s = sbuf;
699	*s++ = C_LOGMSG;
700	while ((nread = read(fd[PIPE_READ], buf, sizeof(buf))) > 0) {
701		cp = buf;
702		do {
703			*s++ = *cp++;
704			if (cp[-1] != '\n') {
705				if (s < (char *) &sbuf[sizeof(sbuf)-1])
706					continue;
707				*s++ = '\n';
708			}
709			/*
710			 * Throw away blank lines.
711			 */
712			if (s == &sbuf[2]) {
713				s--;
714				continue;
715			}
716			if (isserver)
717				(void) xwrite(rem_w, sbuf, s - sbuf);
718			else {
719				*s = CNULL;
720				message(MT_INFO, "%s", sbuf+1);
721			}
722			s = &sbuf[1];
723		} while (--nread);
724	}
725	if (s > (char *) &sbuf[1]) {
726		*s++ = '\n';
727		if (isserver)
728			(void) xwrite(rem_w, sbuf, s - sbuf);
729		else {
730			*s = CNULL;
731			message(MT_INFO, "%s", sbuf+1);
732		}
733	}
734	while ((wpid = wait(&status)) != pid && wpid != -1)
735		;
736	if (wpid == -1)
737		status = -1;
738	(void) close(fd[PIPE_READ]);
739	if (status)
740		error("shell returned %d", status);
741	else if (isserver)
742		ack();
743}
744
745/*
746 * Malloc with error checking
747 */
748void *
749xmalloc(size_t amt)
750{
751	void *ptr;
752
753	if ((ptr = malloc(amt)) == NULL)
754		fatalerr("Cannot malloc %zu bytes of memory.", amt);
755
756	return (ptr);
757}
758
759/*
760 * realloc with error checking
761 */
762void *
763xrealloc(void *baseptr, size_t amt)
764{
765	void *new;
766
767	if ((new = realloc(baseptr, amt)) == NULL)
768		fatalerr("Cannot realloc %zu bytes of memory.", amt);
769
770	return (new);
771}
772
773/*
774 * calloc with error checking
775 */
776void *
777xcalloc(size_t num, size_t esize)
778{
779	void *ptr;
780
781	if ((ptr = calloc(num, esize)) == NULL)
782		fatalerr("Cannot calloc %zu * %zu = %zu bytes of memory.",
783		      num, esize, num * esize);
784
785	return (ptr);
786}
787
788/*
789 * Strdup with error checking
790 */
791char *
792xstrdup(const char *str)
793{
794	size_t len = strlen(str) + 1;
795	char *nstr = xmalloc(len);
796
797	return (memcpy(nstr, str, len));
798}
799
800/*
801 * Private version of basename()
802 */
803char *
804xbasename(char *path)
805{
806	char *cp;
807
808	if ((cp = strrchr(path, '/')) != NULL)
809		return(cp+1);
810	else
811		return(path);
812}
813
814/*
815 * Take a colon (':') separated path to a file and
816 * search until a component of that path is found and
817 * return the found file name.
818 */
819char *
820searchpath(char *path)
821{
822	char *file;
823	char *space;
824	int found;
825	struct stat statbuf;
826
827	for (found = 0; !found && (file = strsep(&path, ":")) != NULL; ) {
828		if ((space = strchr(file, ' ')) != NULL)
829			*space = CNULL;
830		found = stat(file, &statbuf) == 0;
831		if (space)
832			*space = ' ';		/* Put back what we zapped */
833	}
834	return (file);
835}
836