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
ValidUser(host,uid,gid,error,shell,dir,rst)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
AllocatePty(socket0,socket1)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
OpenPtySlave()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
DoHelper(pfd0,pfd1,pfd2)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
KillHelper(int grp)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
LoginUser(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
LogoutUser(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
SetPtyMode(mode)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
SetPtySize(struct rex_ttysize * sizep)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
SendSignal(int sig)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
HelperRead(pollfd_t * fdp,int nfds,int * pollretval)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