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