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 (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26/*	Copyright (c) 1983-1989 AT&T	*/
27/*	  All Rights Reserved  	*/
28
29/*
30 * Portions of this source code were derived from Berkeley 4.3 BSD
31 * under license from the Regents of the University of California.
32 */
33
34#include <sys/types.h>
35#include <sys/ioctl.h>
36#include <sys/param.h>
37#include <sys/socket.h>
38#include <sys/time.h>
39#include <sys/filio.h>
40
41#include <netinet/in.h>
42#include <arpa/inet.h>
43
44#include <unistd.h>
45#include <string.h>
46#include <stdlib.h>
47#include <stdio.h>
48#include <stdarg.h>
49#include <errno.h>
50#include <pwd.h>
51#include <grp.h>
52#include <signal.h>
53#include <netdb.h>
54#include <syslog.h>
55#include <nss_dbdefs.h>
56#include <security/pam_appl.h>
57#include <deflt.h>
58
59#ifdef SYSV
60#include <shadow.h>
61#endif /* SYSV */
62
63#ifndef NCARGS
64#define	NCARGS	5120
65#endif /* NCARGS */
66
67#ifdef SYSV
68#define	rindex	strrchr
69#define	killpg(a, b)	kill(-(a), (b))
70#else
71char  *sprintf();
72#endif	/* SYSV */
73
74#define	MAXFD(A, B) ((A) > (B) ? (A) : (B))
75#define	_PATH_DEFAULT_LOGIN	"/etc/default/login"
76
77static void error(char *fmt, ...);
78static void doit(int f, struct sockaddr_storage *fromp);
79static void getstr(char *buf, int cnt, char *err);
80
81static int legalenvvar(char *s);
82
83/* Function decls. for functions not in any header file.  (Grrrr.) */
84extern int audit_rexecd_setup(void);
85extern int audit_rexecd_success(char *, char *, char *);
86extern int audit_rexecd_fail(char *, char *, char *, char *);
87extern int audit_settid(int);	/* set termnal ID */
88
89/* PAM conversation function */
90static int rexec_conv(int, struct pam_message **,
91		struct pam_response **, void *);
92
93static pam_handle_t *pamh;	/* authentication handle */
94static struct pam_conv conv = {
95			rexec_conv,
96			NULL
97		};
98
99/*
100 * remote execute server:
101 *	username\0
102 *	password\0
103 *	command\0
104 *	data
105 *
106 * in.rexecd has been modified to run as the user invoking it. Hence there is no
107 * need to limit any privileges.
108 */
109/*ARGSUSED*/
110int
111main(int argc, char **argv)
112{
113	struct sockaddr_storage from;
114	socklen_t fromlen;
115
116	openlog("rexec", LOG_PID | LOG_ODELAY, LOG_DAEMON);
117	(void) audit_rexecd_setup();	/* BSM */
118	fromlen = (socklen_t)sizeof (from);
119	if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) {
120		(void) fprintf(stderr, "%s: ", argv[0]);
121		perror("getpeername");
122		exit(1);
123	}
124
125	if (audit_settid(0) != 0) {
126		perror("settid");
127		exit(1);
128	}
129
130	doit(0, &from);
131	return (0);
132}
133
134static char	username[20] = "USER=";
135static char	homedir[64] = "HOME=";
136static char	shell[64] = "SHELL=";
137
138static char	*envinit[] =
139#ifdef SYSV
140	{homedir, shell, (char *)0, username,
141	(char *)0, (char *)0, (char *)0, (char *)0,
142	(char *)0, (char *)0, (char *)0, (char *)0,
143	(char *)0, (char *)0, (char *)0, (char *)0,
144	(char *)0, (char *)0, (char *)0, (char *)0,
145	(char *)0};
146#define	ENVINIT_PATH	2	/* position of PATH in envinit[] */
147#define	PAM_ENV_ELIM	16	/* max PAM environment variables */
148
149/*
150 *	See PSARC opinion 1992/025
151 */
152static char	userpath[] = "PATH=/usr/bin:";
153static char	rootpath[] = "PATH=/usr/sbin:/usr/bin";
154#else
155	    {homedir, shell, "PATH=:/usr/ucb:/bin:/usr/bin", username, 0};
156#endif /* SYSV */
157
158static struct	sockaddr_storage asin;
159static char pass[16];
160
161static void
162doit(int f, struct sockaddr_storage *fromp)
163{
164	char cmdbuf[NCARGS+1], *cp;
165	char user[16];
166	char hostname [MAXHOSTNAMELEN + 1];
167	struct passwd *pwd, pw_data;
168	char pwdbuf[NSS_BUFLEN_PASSWD];
169	int s;
170	ushort_t port;
171	pid_t pid;
172	int pv[2], cc;
173	fd_set readfrom, ready;
174	char buf[BUFSIZ], sig;
175	int one = 1;
176	int idx = 0, end_env = 0;
177	char **pam_env;
178	int status = PAM_AUTH_ERR;
179	char abuf[INET6_ADDRSTRLEN];
180	struct in_addr v4dst;
181	socklen_t fromplen;
182	struct sockaddr_in *sin;
183	struct sockaddr_in6 *sin6;
184	int pam_flags = 0;
185
186	(void) signal(SIGINT, SIG_DFL);
187	(void) signal(SIGQUIT, SIG_DFL);
188	(void) signal(SIGTERM, SIG_DFL);
189#ifdef DEBUG
190	{
191		int t = open("/dev/tty", 2);
192		if (t >= 0) {
193#ifdef SYSV
194			(void) setsid();
195#else
196			(void) ioctl(t, TIOCNOTTY, (char *)0);
197#endif	/* SYSV */
198			(void) close(t);
199		}
200	}
201#endif
202	if (fromp->ss_family == AF_INET) {
203		sin = (struct sockaddr_in *)fromp;
204		fromplen = sizeof (struct sockaddr_in);
205		asin.ss_family = AF_INET;  /* used for bind */
206	} else if (fromp->ss_family == AF_INET6) {
207		sin6 = (struct sockaddr_in6 *)fromp;
208		fromplen = sizeof (struct sockaddr_in6);
209		asin.ss_family = AF_INET6; /* used for bind */
210	} else {
211		syslog(LOG_ERR, "unknown address family %d\n",
212		    fromp->ss_family);
213		exit(1);
214	}
215	/*
216	 * store common info. for audit record
217	 */
218
219	if (getnameinfo((const struct sockaddr *) fromp, fromplen, hostname,
220	    sizeof (hostname), NULL, 0, 0) != 0) {
221		if (fromp->ss_family == AF_INET6) {
222			if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
223				struct in_addr ipv4_addr;
224
225				IN6_V4MAPPED_TO_INADDR(&sin6->sin6_addr,
226				    &ipv4_addr);
227				inet_ntop(AF_INET, &ipv4_addr, abuf,
228				    sizeof (abuf));
229			} else {
230				inet_ntop(AF_INET6, &sin6->sin6_addr,
231				    abuf, sizeof (abuf));
232			}
233		} else if (fromp->ss_family == AF_INET) {
234				inet_ntop(AF_INET, &sin->sin_addr,
235				    abuf, sizeof (abuf));
236			}
237		(void) strncpy(hostname, abuf, sizeof (hostname));
238	}
239	(void) dup2(f, 0);
240	(void) dup2(f, 1);
241	(void) dup2(f, 2);
242	(void) alarm(60);
243	port = 0;
244	for (;;) {
245		char c;
246		if (read(f, &c, 1) != 1)
247			exit(1);
248		if (c == 0)
249			break;
250		port = port * 10 + c - '0';
251	}
252	(void) alarm(0);
253	if (port != 0) {
254		s = socket(fromp->ss_family, SOCK_STREAM, 0);
255		if (s < 0)
256			exit(1);
257		if (bind(s, (struct sockaddr *)&asin, fromplen) < 0)
258			exit(1);
259		(void) alarm(60);
260		if (fromp->ss_family == AF_INET) {
261			sin->sin_port = htons((ushort_t)port);
262		} else if (fromp->ss_family == AF_INET6) {
263			sin6->sin6_port = htons((ushort_t)port);
264		}
265		if (connect(s, (struct sockaddr *)fromp, fromplen) < 0)
266			exit(1);
267		(void) alarm(0);
268	}
269	getstr(user, sizeof (user), "username");
270	getstr(pass, sizeof (pass), "password");
271	getstr(cmdbuf, sizeof (cmdbuf), "command");
272
273	pwd = getpwnam_r(user, &pw_data, pwdbuf, sizeof (pwdbuf));
274	if (pwd == NULL) {
275		(void) audit_rexecd_fail("Login incorrect", hostname, user,
276		    cmdbuf);	    /* BSM */
277		error("Login incorrect.\n");
278		exit(1);
279	}
280
281	if (defopen(_PATH_DEFAULT_LOGIN) == 0) {
282		int flags;
283		char *p;
284		flags = defcntl(DC_GETFLAGS, 0);
285		TURNOFF(flags, DC_CASE);
286		(void) defcntl(DC_SETFLAGS, flags);
287		if ((p = defread("PASSREQ=")) != NULL &&
288		    strcasecmp(p, "YES") == 0) {
289			pam_flags |= PAM_DISALLOW_NULL_AUTHTOK;
290		}
291		defopen(NULL);
292	}
293
294	if (pam_start("rexec", user, &conv, &pamh) != PAM_SUCCESS) {
295		exit(1);
296	}
297	if (pam_set_item(pamh, PAM_RHOST, hostname) != PAM_SUCCESS) {
298		exit(1);
299	}
300
301	if ((status = pam_authenticate(pamh, pam_flags)) != PAM_SUCCESS) {
302		switch (status) {
303		case PAM_USER_UNKNOWN:
304			(void) audit_rexecd_fail("Login incorrect", hostname,
305			    user, cmdbuf);		/* BSM */
306			error("Login incorrect.\n");
307			break;
308		default:
309			(void) audit_rexecd_fail("Password incorrect", hostname,
310			    user, cmdbuf);	/* BSM */
311			error("Password incorrect.\n");
312		}
313		pam_end(pamh, status);
314		exit(1);
315	}
316	if ((status = pam_acct_mgmt(pamh, pam_flags)) != PAM_SUCCESS) {
317		(void) audit_rexecd_fail("Account or Password Expired",
318		    hostname, user, cmdbuf);
319		switch (status) {
320			case PAM_NEW_AUTHTOK_REQD:
321				error("Password Expired.\n");
322				break;
323			case PAM_PERM_DENIED:
324				error("Account Expired.\n");
325				break;
326			case PAM_AUTHTOK_EXPIRED:
327				error("Password Expired.\n");
328				break;
329			default:
330				error("Login incorrect.\n");
331				break;
332		}
333		pam_end(pamh, status);
334		exit(1);
335	}
336
337	(void) write(2, "\0", 1);
338
339	if (setgid((gid_t)pwd->pw_gid) < 0) {
340		(void) audit_rexecd_fail("Can't setgid", hostname,
341		    user, cmdbuf);	/* BSM */
342		error("setgid");
343		pam_end(pamh, PAM_ABORT);
344		exit(1);
345	}
346	(void) initgroups(pwd->pw_name, pwd->pw_gid);
347
348	if ((status = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) {
349		(void) audit_rexecd_fail("Unable to establish credentials",
350		    hostname, user, cmdbuf);	/* BSM */
351		error("Unable to establish credentials.\n");
352		pam_end(pamh, PAM_SUCCESS);
353	}
354
355	(void) audit_rexecd_success(hostname, user, cmdbuf);	/* BSM */
356
357	if (setuid((uid_t)pwd->pw_uid) < 0) {
358		(void) audit_rexecd_fail("Can't setuid", hostname,
359		    user, cmdbuf);	/* BSM */
360		error("setuid");
361		pam_end(pamh, PAM_ABORT);
362		exit(1);
363	}
364
365
366	if (port) {
367		(void) pipe(pv);
368		pid = fork();
369		if (pid == (pid_t)-1)  {
370			error("Try again.\n");
371			pam_end(pamh, PAM_ABORT);
372			exit(1);
373		}
374		if (pid) {
375			/*
376			 * since the daemon is running as the user no need
377			 * to prune privileges.
378			 */
379			(void) close(0); (void) close(1); (void) close(2);
380			(void) close(f); (void) close(pv[1]);
381			FD_ZERO(&readfrom);
382			FD_SET(s, &readfrom);
383			FD_SET(pv[0], &readfrom);
384			(void) ioctl(pv[0], FIONBIO, (char *)&one);
385			/* should set s nbio! */
386			do {
387				ready = readfrom;
388				if (select(MAXFD(s, pv[0])+1, &ready, NULL,
389				    NULL, NULL) < 0) {
390					perror("select:");
391					exit(1);
392				}
393				if (FD_ISSET(s, &ready)) {
394					if (read(s, &sig, 1) <= 0)
395						FD_CLR(s, &readfrom);
396					else
397						(void) killpg(pid, sig);
398				}
399				if (FD_ISSET(pv[0], &ready)) {
400					cc = read(pv[0], buf, sizeof (buf));
401					if (cc <= 0) {
402						(void) shutdown(s, 1+1);
403						FD_CLR(pv[0], &readfrom);
404					} else
405						(void) write(s, buf, cc);
406				}
407			} while (FD_ISSET(s, &readfrom) ||
408			    FD_ISSET(pv[0], &readfrom));
409			exit(0);
410		}
411		/* setpgrp(0, getpid()); */
412		(void) setsid();	/* Should be the same as above. */
413		(void) close(s); (void)close(pv[0]);
414		(void) dup2(pv[1], 2);
415	}
416
417	if (*pwd->pw_shell == '\0')
418		pwd->pw_shell = "/bin/sh";
419	if (f > 2)
420		(void) close(f);
421	/* Change directory only after becoming the appropriate user. */
422	if (chdir(pwd->pw_dir) < 0) {
423		error("No remote directory.\n");
424		pam_end(pamh, PAM_ABORT);
425		exit(1);
426	}
427#ifdef	SYSV
428	if (pwd->pw_uid)
429		envinit[ENVINIT_PATH] = userpath;
430	else
431		envinit[ENVINIT_PATH] = rootpath;
432#endif	/* SYSV */
433	(void) strncat(homedir, pwd->pw_dir, sizeof (homedir) - 6);
434	(void) strncat(shell, pwd->pw_shell, sizeof (shell) - 7);
435	(void) strncat(username, pwd->pw_name, sizeof (username) - 6);
436
437	/*
438	 * add PAM environment variables set by modules
439	 * -- only allowed 16 (PAM_ENV_ELIM)
440	 * -- check to see if the environment variable is legal
441	 */
442	for (end_env = 0; envinit[end_env] != 0; end_env++)
443		;
444	if ((pam_env = pam_getenvlist(pamh)) != 0) {
445		while (pam_env[idx] != 0) {
446			if (idx < PAM_ENV_ELIM &&
447			    legalenvvar(pam_env[idx])) {
448				envinit[end_env + idx] = pam_env[idx];
449			}
450			idx++;
451		}
452	}
453
454	pam_end(pamh, PAM_SUCCESS);
455
456	cp = rindex(pwd->pw_shell, '/');
457	if (cp)
458		cp++;
459	else
460		cp = pwd->pw_shell;
461	(void) execle(pwd->pw_shell, cp, "-c", cmdbuf, (char *)0, envinit);
462	perror(pwd->pw_shell);
463	exit(1);
464}
465
466static void
467getstr(char *buf, int cnt, char *err)
468{
469	char c;
470
471	do {
472		if (read(0, &c, 1) != 1)
473			exit(1);
474		*buf++ = c;
475		if (--cnt == 0) {
476			error("%s too long\n", err);
477			exit(1);
478		}
479	} while (c != 0);
480}
481
482static void
483error(char *fmt, ...)
484{
485	va_list ap;
486	char buf[BUFSIZ];
487
488	buf[0] = 1;
489	va_start(ap, fmt);
490	(void) vsprintf(buf+1, fmt, ap);
491	va_end(ap);
492	(void) write(2, buf, strlen(buf));
493}
494
495static char *illegal[] = {
496	"SHELL=",
497	"HOME=",
498	"LOGNAME=",
499#ifndef NO_MAIL
500	"MAIL=",
501#endif
502	"CDPATH=",
503	"IFS=",
504	"PATH=",
505	"USER=",
506	0
507};
508
509/*
510 * legalenvvar - can PAM insert this environmental variable?
511 */
512
513static int
514legalenvvar(char *s)
515{
516	register char **p;
517
518	for (p = illegal; *p; p++)
519		if (strncmp(s, *p, strlen(*p)) == 0)
520			return (0);
521
522	if (s[0] == 'L' && s[1] == 'D' && s[2] == '_')
523		return (0);
524
525	return (1);
526}
527
528/*
529 * rexec_conv -  This is the conv (conversation) function called from
530 *	a PAM authentication module to print error messages
531 *	or garner information from the user.
532 */
533
534/* ARGSUSED3 */
535static int
536rexec_conv(int num_msg, struct pam_message **msg,
537    struct pam_response **response, void *appdata_ptr)
538{
539	struct pam_message	*m;
540	struct pam_response	*r;
541	int			i;
542
543	if (num_msg <= 0)
544		return (PAM_CONV_ERR);
545
546	*response = calloc(num_msg, sizeof (struct pam_response));
547	if (*response == NULL)
548		return (PAM_BUF_ERR);
549
550	m = *msg;
551	r = *response;
552
553	if (m->msg_style == PAM_PROMPT_ECHO_OFF) {
554		if (pass[0] != '\0') {
555			r->resp = strdup(pass);
556			if (r->resp == NULL) {
557				/* free responses */
558				r = *response;
559				for (i = 0; i < num_msg; i++, r++) {
560					if (r->resp)
561						free(r->resp);
562				}
563				free(*response);
564				*response = NULL;
565				return (PAM_BUF_ERR);
566			}
567		}
568	}
569
570	return (PAM_SUCCESS);
571}
572