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
71 char *sprintf();
72 #endif /* SYSV */
73
74 #define MAXFD(A, B) ((A) > (B) ? (A) : (B))
75 #define _PATH_DEFAULT_LOGIN "/etc/default/login"
76
77 static void error(char *fmt, ...);
78 static void doit(int f, struct sockaddr_storage *fromp);
79 static void getstr(char *buf, int cnt, char *err);
80
81 static int legalenvvar(char *s);
82
83 /* Function decls. for functions not in any header file. (Grrrr.) */
84 extern int audit_rexecd_setup(void);
85 extern int audit_rexecd_success(char *, char *, char *);
86 extern int audit_rexecd_fail(char *, char *, char *, char *);
87 extern int audit_settid(int); /* set termnal ID */
88
89 /* PAM conversation function */
90 static int rexec_conv(int, struct pam_message **,
91 struct pam_response **, void *);
92
93 static pam_handle_t *pamh; /* authentication handle */
94 static 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*/
110 int
main(int argc,char ** argv)111 main(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
134 static char username[20] = "USER=";
135 static char homedir[64] = "HOME=";
136 static char shell[64] = "SHELL=";
137
138 static 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 */
152 static char userpath[] = "PATH=/usr/bin:";
153 static char rootpath[] = "PATH=/usr/sbin:/usr/bin";
154 #else
155 {homedir, shell, "PATH=:/usr/ucb:/bin:/usr/bin", username, 0};
156 #endif /* SYSV */
157
158 static struct sockaddr_storage asin;
159 static char pass[16];
160
161 static void
doit(int f,struct sockaddr_storage * fromp)162 doit(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
466 static void
getstr(char * buf,int cnt,char * err)467 getstr(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
482 static void
error(char * fmt,...)483 error(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
495 static 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
513 static int
legalenvvar(char * s)514 legalenvvar(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 */
535 static int
rexec_conv(int num_msg,struct pam_message ** msg,struct pam_response ** response,void * appdata_ptr)536 rexec_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