xref: /titanic_51/usr/src/cmd/rexd/unix_login.c (revision 16ab6e0b56ccd36f9a870ff0b87e64c55599a6f0)
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 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #define	BSD_COMP
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <pwd.h>
33 #include <signal.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <unistd.h>
37 
38 #include <sac.h>		/* for SC_WILDC */
39 #include <utmpx.h>
40 
41 #include <rpc/rpc.h>
42 #include <sys/file.h>
43 #include <sys/filio.h>
44 #include <sys/ioctl.h>
45 #include <sys/signal.h>
46 #include <sys/stat.h>
47 #include <sys/types.h>
48 #include <sys/wait.h>
49 
50 /*
51  * # include <sys/label.h>
52  * # include <sys/audit.h>
53  *
54  *
55  *
56  * # include <pwdadj.h>
57  */
58 
59 #include <sys/ttold.h>
60 #include <stropts.h>
61 #include <sys/stream.h>
62 
63 
64 
65 #include "rex.h"
66 
67 #include <security/pam_appl.h>
68 pam_handle_t *pamh;
69 
70 #define	NTTYDISC	2	/* New ttydiscipline: stolen from ttold.h */
71 
72 /*
73  * unix_login - hairy junk to simulate logins for Unix
74  */
75 
76 int	Master,	Slave;			/* sides of the pty */
77 int	Slave_is_closed_on_master_side;
78 
79 static char	*slavename;
80 extern char *ptsname();
81 
82 
83 int	InputSocket,			/* Network sockets */
84 	OutputSocket;
85 int	Helper1,			/* pids of the helpers */
86 	Helper2;
87 char	UserName[256];			/* saves the user name for loging */
88 char	HostName[256];			/* saves the host name for loging */
89 
90 static	int	TtySlot;		/* slot number in Utmpx */
91 
92 /*
93  * pseudo-xprts used to add pty fds to svc_pollfd[]. This allows the
94  * polling for all i/o in one poll().
95  */
96 SVCXPRT uxprt[2];
97 
98 #define	INPUTSOCKET	0		/* InputSocket xprt */
99 #define	MASTER		1		/* Master xprt */
100 
101 
102 extern	int child;		/* pid of the executed process */
103 extern	int ChildDied;		/* flag */
104 extern	int HasHelper;		/* flag */
105 
106 extern	void setproctitle(char *user, char *host);
107 extern int Debug;
108 
109 extern void audit_rexd_fail(char *, char *, char *, uid_t, gid_t,
110 				char *, char **);
111 
112 #define	bzero(s, n)	memset((s), 0, (n))
113 #define	bcopy(a, b, c)	memcpy((b), (a), (c))
114 
115 static void LogoutUser(void);
116 
117 /*
118  * Check for user being able to run on this machine.
119  * returns 0 if OK, TRUE if problem, error message in "error"
120  * copies name of shell and home directory if user is valid.
121  */
122 int
123 ValidUser(host, uid, gid, error, shell, dir, rst)
124 	char *host;		/* passed in */
125 	uid_t uid;
126 	gid_t gid;
127 	char *error;		/* filled in on return */
128 	char *shell;		/* filled in on return */
129 	char *dir;		/* filled in on return */
130 	struct rex_start *rst;	/* passed in */
131 {
132 	struct passwd *pw, *getpwuid();
133 	int v;
134 
135 	pw = getpwuid(uid);
136 	if (pw == NULL || pw->pw_name == NULL)
137 	{
138 		errprintf(error, "rexd: User id %d not valid\n", uid);
139 		audit_rexd_fail("user id is not valid",
140 				host,
141 				NULL,
142 				uid,
143 				gid,
144 				NULL,
145 				rst->rst_cmd);	    /* BSM */
146 		return (1);
147 	}
148 	strncpy(UserName, pw->pw_name, sizeof (UserName) - 1);
149 	strncpy(HostName, host, sizeof (HostName) - 1);
150 	strcpy(shell, pw->pw_shell);
151 	strcpy(dir, pw->pw_dir);
152 	setproctitle(pw->pw_name, host);
153 
154 	if (pam_start("rexd", pw->pw_name, NULL, &pamh) != PAM_SUCCESS ||
155 	    pam_set_item(pamh, PAM_RHOST, host) != PAM_SUCCESS) {
156 		audit_rexd_fail("user id is not valid",
157 				host,
158 				pw->pw_name,
159 				uid,
160 				gid,
161 				shell,
162 				rst->rst_cmd);	    /* BSM */
163 		errprintf(error, "rexd: User id %d not valid\n", uid);
164 		if (pamh) {
165 			pam_end(pamh, PAM_ABORT);
166 			pamh = NULL;
167 		}
168 		return (1);
169 	}
170 
171 	if ((v = pam_acct_mgmt(pamh, 0)) != PAM_SUCCESS) {
172 		switch (v) {
173 		case PAM_NEW_AUTHTOK_REQD:
174 			errprintf(error,
175 				"rexd: User id %d Password Expired\n", uid);
176 			break;
177 		case PAM_PERM_DENIED:
178 			errprintf(error,
179 				"rexd: User id %d Account Expired\n", uid);
180 			break;
181 		case PAM_AUTHTOK_EXPIRED:
182 			errprintf(error,
183 				"rexd: User id %d Password Expired\n", uid);
184 			break;
185 		default:
186 			errprintf(error,
187 				"rexd: User id %d not valid\n", uid);
188 			break;
189 		}
190 		pam_end(pamh, PAM_ABORT);
191 		pamh = NULL;
192 
193 		audit_rexd_fail("user account expired",
194 				host,
195 				pw->pw_name,
196 				uid,
197 				gid,
198 				shell,
199 				rst->rst_cmd);	    /* BSM */
200 		return (1);
201 	}
202 
203 	return (0);
204 }
205 
206 /*
207  * Add an audit record with argv that was pre-set, plus the given string
208  */
209 
210 /*
211  * Allocate a pseudo-terminal
212  * sets the global variables Master and Slave.
213  * returns 1 on error, 0 if OK
214  */
215 int
216 AllocatePty(socket0, socket1)
217 	int socket0, socket1;
218 {
219 
220 	int on = 1;
221 
222 	sigset(SIGHUP, SIG_IGN);
223 	sigset(SIGTTOU, SIG_IGN);
224 	sigset(SIGTTIN, SIG_IGN);
225 
226 	if ((Master = open("/dev/ptmx", O_RDWR)) == -1) {
227 	    if (Debug)
228 		    printf("open-ptmx-failure\n");
229 	    perror("AloocatePtyMaster fails");
230 	    return (1);		/* error could not open /dev/ptmx */
231 	}
232 	if (Debug)
233 	    printf("open-ptmx success Master =%d\n", Master);
234 	if (Debug)
235 	    printf("Before grantpt...Master=%d\n", Master);
236 
237 	if (grantpt(Master) == -1) {
238 	    perror("could not grant slave pty");
239 	    exit(1);
240 	}
241 	if (unlockpt(Master) == -1) {
242 	    perror("could not unlock slave pty");
243 	    exit(1);
244 	}
245 	if ((slavename = ptsname(Master)) == NULL) {
246 	    perror("could not enable slave pty");
247 	    exit(1);
248 	}
249 	if ((Slave = open(slavename, O_RDWR)) == -1) {
250 	    perror("could not open slave pty");
251 	    exit(1);
252 	}
253 	if (ioctl(Slave, I_PUSH, "ptem") == -1) {
254 	    perror("ioctl I_PUSH ptem");
255 	    exit(1);
256 	}
257 	if (ioctl(Slave, I_PUSH, "ldterm") == -1) {
258 	    perror("ioctl I_PUSH ldterm");
259 	    exit(1);
260 	}
261 	if (ioctl(Slave, I_PUSH, "ttcompat") == -1) {
262 	    perror("ioctl I_PUSH ttcompat");
263 	    exit(1);
264 	}
265 
266 	Slave_is_closed_on_master_side = FALSE;
267 	setsid(); /* get rid of controlling terminal */
268 	/* LoginUser(); */
269 
270 	InputSocket = socket0;
271 	OutputSocket = socket1;
272 	ioctl(Master, FIONBIO, &on);
273 	uxprt[INPUTSOCKET].xp_fd = InputSocket;
274 	xprt_register(&uxprt[INPUTSOCKET]);
275 	uxprt[MASTER].xp_fd = Master;
276 	xprt_register(&uxprt[MASTER]);
277 	return (0);
278 
279 }
280 
281 void
282 OpenPtySlave()
283 {
284 	close(Slave);
285 	Slave = open(slavename, O_RDWR);
286 	if (Slave < 0) {
287 		perror(slavename);
288 		exit(1);
289 	}
290 }
291 
292 
293 
294 	/*
295 	 * Special processing for interactive operation.
296 	 * Given pointers to three standard file descriptors,
297 	 * which get set to point to the pty.
298 	 */
299 void
300 DoHelper(pfd0, pfd1, pfd2)
301 	int *pfd0, *pfd1, *pfd2;
302 {
303 	int pgrp;
304 
305 
306 	sigset(SIGINT, SIG_IGN);
307 	close(Master);
308 	close(InputSocket);
309 	close(OutputSocket);
310 
311 	*pfd0 = Slave;
312 	*pfd1 = Slave;
313 	*pfd2 = Slave;
314 }
315 
316 
317 /*
318  * destroy the helpers when the executing process dies
319  */
320 void
321 KillHelper(int grp)
322 {
323 	if (Debug)
324 		printf("Enter KillHelper\n");
325 	close(Master);
326 	xprt_unregister(&uxprt[MASTER]);
327 	close(InputSocket);
328 	xprt_unregister(&uxprt[INPUTSOCKET]);
329 	close(OutputSocket);
330 	LogoutUser();
331 
332 	if (grp)
333 	    kill((-grp), SIGKILL);
334 }
335 
336 
337 /*
338  * edit the Unix traditional data files that tell who is logged
339  * into "the system"
340  */
341 unsigned char	utid[] = {'o', 'n', SC_WILDC, SC_WILDC};
342 
343 void
344 LoginUser(void)
345 {
346 
347 	char *user;
348 	char *rhost;
349 	/* the next 4 variables are needed for utmpx mgmt */
350 	int		tmplen;
351 	struct utmpx	*u = NULL;
352 	struct utmpx	set_utmp;
353 	char		*ttyntail;
354 
355 	/* We're pretty drastic here, exiting if an error is detected */
356 	if (pam_set_item(pamh, PAM_TTY, slavename)	!= PAM_SUCCESS ||
357 	    pam_get_item(pamh, PAM_USER, (void **) &user) != PAM_SUCCESS ||
358 	    pam_get_item(pamh, PAM_RHOST, (void **) &rhost) != PAM_SUCCESS ||
359 	    pam_open_session(pamh, 0)			!= PAM_SUCCESS) {
360 		/*
361 		 * XXX should print something but for now we exit
362 		 */
363 		exit(1);
364 	}
365 
366 	(void) memset((void *)&set_utmp, 0, sizeof (set_utmp));
367 	(void) time(&set_utmp.ut_tv.tv_sec);
368 	set_utmp.ut_pid = getpid();
369 	if (rhost != NULL && rhost[0] != '\0') {
370 		(void) strcpy(set_utmp.ut_host, rhost);
371 		tmplen = strlen(rhost) + 1;
372 		if (tmplen < sizeof (set_utmp.ut_host))
373 			set_utmp.ut_syslen = tmplen;
374 		else
375 			set_utmp.ut_syslen = sizeof (set_utmp.ut_host);
376 	} else {
377 		(void) memset(set_utmp.ut_host, 0, sizeof (set_utmp.ut_host));
378 		set_utmp.ut_syslen = 0;
379 	}
380 	(void) strcpy(set_utmp.ut_user, user);
381 
382 	/*
383 	 * Copy in the name of the tty minus the "/dev/" if a /dev/ is
384 	 * in the path name.
385 	 */
386 	ttyntail = slavename;
387 	if (strstr(ttyntail, "/dev/") != 0)
388 		ttyntail = ttyntail + strlen("/dev/");
389 	(void) strcpy(set_utmp.ut_line, ttyntail);
390 
391 	set_utmp.ut_type = USER_PROCESS;
392 	if (utid != NULL)
393 		(void) memcpy(set_utmp.ut_id, utid, sizeof (set_utmp.ut_id));
394 	/*
395 	 * Go through each entry one by one, looking only at INIT,
396 	 * LOGIN or USER Processes.  Use the entry found if flags == 0
397 	 * and the line name matches, or if the process ID matches if
398 	 * the UPDATE_ENTRY flag is set.  The UPDATE_ENTRY flag is mainly
399 	 * for login which normally only wants to update an entry if
400 	 * the pid fields matches.
401 	 */
402 
403 	if (u == (struct utmpx *)NULL) {
404 		(void) makeutx(&set_utmp);
405 	} else
406 		updwtmpx(WTMPX_FILE, &set_utmp);
407 
408 }
409 
410 /*
411  * edit the Unix traditional data files that tell who is logged
412  * into "the system".
413  */
414 static void
415 LogoutUser(void)
416 {
417 	struct utmpx *up;
418 	struct utmpx ut;
419 	int pid;
420 	char user[sizeof (ut.ut_user) + 1];
421 	char ttyn[sizeof (ut.ut_line) + 1];
422 	char rhost[sizeof (ut.ut_host) + 1];
423 
424 	sighold(SIGCHLD);		/* no disruption during cleanup */
425 
426 	if (pamh) {
427 		pam_end(pamh, PAM_SUCCESS);
428 		pamh = NULL;
429 	}
430 
431 	/* BEGIN RESET UTMP */
432 	pid = child;
433 	setutxent();
434 	while (up = getutxent()) {
435 		if (up->ut_pid == pid) {
436 			if (up->ut_type == DEAD_PROCESS) {
437 				/*
438 				 * Cleaned up elsewhere.
439 				 */
440 				break;
441 			}
442 
443 			strncpy(user, up->ut_user, sizeof (up->ut_user));
444 			user[sizeof (up->ut_user)] = '\0';
445 			strncpy(ttyn, up->ut_line, sizeof (up->ut_line));
446 			ttyn[sizeof (up->ut_line)] = '\0';
447 			strncpy(rhost, up->ut_host, sizeof (up->ut_host));
448 			rhost[sizeof (up->ut_host)] = '\0';
449 
450 			if ((pam_start("rexd", user, NULL, &pamh))
451 							== PAM_SUCCESS) {
452 				(void) pam_set_item(pamh, PAM_TTY, ttyn);
453 				(void) pam_set_item(pamh, PAM_RHOST, rhost);
454 				(void) pam_close_session(pamh, 0);
455 				(void) pam_end(pamh, PAM_SUCCESS);
456 				pamh = NULL;
457 			}
458 
459 			up->ut_type = DEAD_PROCESS;
460 			up->ut_exit.e_termination = WTERMSIG(0);
461 			up->ut_exit.e_exit = WEXITSTATUS(0);
462 			(void) time(&up->ut_tv.tv_sec);
463 			if (modutx(up) == NULL) {
464 				/*
465 				 * Since modutx failed we'll
466 				 * write out the new entry
467 				 * ourselves.
468 				 */
469 				(void) pututxline(up);
470 				updwtmpx("wtmpx", up);
471 			}
472 			break;
473 		}
474 	}
475 	endutxent();
476 	/* END RESET UTMP */
477 	sigrelse(SIGCHLD);
478 }
479 
480 /*
481  * set the pty modes to the given values
482  */
483 void
484 SetPtyMode(mode)
485 	struct rex_ttymode *mode;
486 {
487 	struct sgttyb svr4_sgttyb_var;
488 	int ldisc = NTTYDISC;
489 
490 	if (Debug)
491 		printf("Enter SetPtyMode\n");
492 	if (Debug)
493 		printf("SetPtyMode:opened slave\n");
494 	ioctl(Slave, TIOCSETD, &ldisc);
495 	if (Debug)
496 		printf("SetPtyMode:Slave TIOCSETD done\n");
497 
498 	/*
499 	 * Copy from over-the-net(bsd) to SVR4 format
500 	 */
501 	svr4_sgttyb_var.sg_ispeed = mode->basic.sg_ispeed;
502 	svr4_sgttyb_var.sg_ospeed = mode->basic.sg_ospeed;
503 	svr4_sgttyb_var.sg_erase  = mode->basic.sg_erase;
504 	svr4_sgttyb_var.sg_kill = mode->basic.sg_kill;
505 	svr4_sgttyb_var.sg_flags = (int)mode->basic.sg_flags;
506 	/*
507 	 * Clear any possible sign extension caused by (int)
508 	 * typecast
509 	 */
510 	svr4_sgttyb_var.sg_flags &= 0xFFFF;
511 
512 	ioctl(Slave, TIOCSETN, &svr4_sgttyb_var);
513 	if (Debug)
514 		printf("SetPtyMode:Slave TIOCSETN done\n");
515 	ioctl(Slave, TIOCSETC, &mode->more);
516 	if (Debug)
517 		printf("SetPtyMode:Slave TIOCSETC done\n");
518 	ioctl(Slave, TIOCSLTC, &mode->yetmore);
519 	if (Debug)
520 		printf("SetPtyMode:Slave TIOCSLTC done\n");
521 	ioctl(Slave, TIOCLSET, &mode->andmore);
522 	if (Debug)
523 		printf("SetPtyMode:Slave TIOCSET done\n");
524 
525 	/* Opened in AllocPty for parent, still open in child */
526 	if (Slave_is_closed_on_master_side == FALSE) {
527 		close(Slave);
528 		Slave_is_closed_on_master_side = TRUE;
529 	}
530 }
531 
532 /*
533  * set the pty window size to the given value
534  */
535 void
536 SetPtySize(struct rex_ttysize *sizep)
537 {
538 	struct winsize newsize;
539 
540 	/* if size has changed, this ioctl changes it */
541 	/* *and* sends SIGWINCH to process group */
542 
543 	newsize.ws_row = (unsigned short) sizep->ts_lines;
544 	newsize.ws_col = (unsigned short) sizep->ts_cols;
545 
546 	(void) ioctl(Master, TIOCSWINSZ, &newsize);
547 	if (Slave_is_closed_on_master_side == FALSE) {
548 		close(Slave);
549 		Slave_is_closed_on_master_side = TRUE;
550 	}
551 }
552 
553 
554 /*
555  * send the given signal to the group controlling the terminal
556  */
557 void
558 SendSignal(int sig)
559 {
560 	pid_t pgrp;
561 
562 	pgrp = getpgid(child);
563 	if (pgrp != (pid_t)-1)
564 		(void) kill((-pgrp), sig);
565 }
566 
567 /*
568  * called when the main select loop detects that we might want to
569  * read something.
570  */
571 void
572 HelperRead(pollfd_t *fdp, int nfds, int *pollretval)
573 {
574 	char buf[128];
575 	int retval;
576 	extern int errno;
577 	int mask;
578 	int master = -1;
579 	int inputsocket = -1;
580 
581 	/*
582 	 * fdp pollset may be compressed. Search for Master and
583 	 * InputSocket fds.
584 	 */
585 	int i;
586 	for (i = 0; i < nfds; i++) {
587 		if (fdp[i].fd == Master && fdp[i].revents != 0)
588 			master = i;
589 		if (fdp[i].fd == InputSocket && fdp[i].revents != 0)
590 			inputsocket = i;
591 	}
592 
593 /*	mask = sigsetmask (sigmask (SIGCHLD));	*/
594 	mask = sighold(SIGCHLD);
595 	retval = 0;
596 	if (master != -1) {
597 		if (!(fdp[master].revents & (POLLERR | POLLHUP | POLLNVAL))) {
598 			retval = read(Master, buf, sizeof (buf));
599 			if (retval > 0) {
600 				(void) write(OutputSocket, buf, retval);
601 			} else {
602 				if (errno != EINTR && errno != EIO &&
603 				    errno != EWOULDBLOCK)
604 					perror("pty read");
605 				/* 1 => further sends disallowed */
606 				shutdown(OutputSocket, 1);
607 				xprt_unregister(&uxprt[MASTER]);
608 			}
609 		}
610 
611 		/* clear this event for svc_getreq_poll */
612 		fdp[master].revents = 0;
613 		*pollretval = *pollretval - 1;
614 
615 		if (retval <= 0 && ChildDied) {
616 			KillHelper(child);
617 			HasHelper = 0;
618 			if (inputsocket != -1) {
619 				fdp[inputsocket].revents = 0;
620 				*pollretval = *pollretval - 1;
621 			}
622 			goto done;
623 		}
624 	}
625 
626 	if (inputsocket != -1) {
627 		if (!(fdp[inputsocket].revents & (POLLERR | POLLHUP |
628 							    POLLNVAL))) {
629 			retval = read(InputSocket, buf, sizeof (buf));
630 			if (retval > 0) {
631 				(void) write(Master, buf, retval);
632 			} else {
633 				if (errno != EINTR && errno != EWOULDBLOCK)
634 					perror("socket read");
635 				xprt_unregister(&uxprt[INPUTSOCKET]);
636 			}
637 		}
638 
639 		/* clear this event for svc_getreq_poll */
640 		fdp[inputsocket].revents = 0;
641 		*pollretval = *pollretval - 1;
642 	}
643 
644 	done:
645 /*	sigsetmask (mask);	*/
646 	sigrelse(SIGCHLD);
647 }
648