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.
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.
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]
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 /* Copyright (c) 1983-1989 AT&T */
27 /* All Rights Reserved */
30 * Portions of this source code were derived from Berkeley 4.3 BSD
31 * under license from the Regents of the University of California.
34 #include <sys/types.h>
35 #include <sys/ioctl.h>
36 #include <sys/param.h>
37 #include <sys/socket.h>
39 #include <sys/filio.h>
41 #include <netinet/in.h>
42 #include <arpa/inet.h>
55 #include <nss_dbdefs.h>
56 #include <security/pam_appl.h>
68 #define rindex strrchr
69 #define killpg(a, b) kill(-(a), (b))
74 #define MAXFD(A, B) ((A) > (B) ? (A) : (B))
75 #define _PATH_DEFAULT_LOGIN "/etc/default/login"
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
);
81 static int legalenvvar(char *s
);
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 */
89 /* PAM conversation function */
90 static int rexec_conv(int, struct pam_message
**,
91 struct pam_response
**, void *);
93 static pam_handle_t
*pamh
; /* authentication handle */
94 static struct pam_conv conv
= {
100 * remote execute server:
106 * in.rexecd has been modified to run as the user invoking it. Hence there is no
107 * need to limit any privileges.
111 main(int argc
, char **argv
)
113 struct sockaddr_storage from
;
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");
125 if (audit_settid(0) != 0) {
134 static char username
[20] = "USER=";
135 static char homedir
[64] = "HOME=";
136 static char shell
[64] = "SHELL=";
138 static char *envinit
[] =
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,
146 #define ENVINIT_PATH 2 /* position of PATH in envinit[] */
147 #define PAM_ENV_ELIM 16 /* max PAM environment variables */
150 * See PSARC opinion 1992/025
152 static char userpath
[] = "PATH=/usr/bin:";
153 static char rootpath
[] = "PATH=/usr/sbin:/usr/bin";
155 {homedir
, shell
, "PATH=:/usr/ucb:/bin:/usr/bin", username
, 0};
158 static struct sockaddr_storage asin
;
159 static char pass
[16];
162 doit(int f
, struct sockaddr_storage
*fromp
)
164 char cmdbuf
[NCARGS
+1], *cp
;
166 char hostname
[MAXHOSTNAMELEN
+ 1];
167 struct passwd
*pwd
, pw_data
;
168 char pwdbuf
[NSS_BUFLEN_PASSWD
];
173 fd_set readfrom
, ready
;
174 char buf
[BUFSIZ
], sig
;
176 int idx
= 0, end_env
= 0;
178 int status
= PAM_AUTH_ERR
;
179 char abuf
[INET6_ADDRSTRLEN
];
180 struct in_addr v4dst
;
182 struct sockaddr_in
*sin
;
183 struct sockaddr_in6
*sin6
;
186 (void) signal(SIGINT
, SIG_DFL
);
187 (void) signal(SIGQUIT
, SIG_DFL
);
188 (void) signal(SIGTERM
, SIG_DFL
);
191 int t
= open("/dev/tty", 2);
196 (void) ioctl(t
, TIOCNOTTY
, (char *)0);
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 */
211 syslog(LOG_ERR
, "unknown address family %d\n",
216 * store common info. for audit record
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
;
225 IN6_V4MAPPED_TO_INADDR(&sin6
->sin6_addr
,
227 inet_ntop(AF_INET
, &ipv4_addr
, abuf
,
230 inet_ntop(AF_INET6
, &sin6
->sin6_addr
,
231 abuf
, sizeof (abuf
));
233 } else if (fromp
->ss_family
== AF_INET
) {
234 inet_ntop(AF_INET
, &sin
->sin_addr
,
235 abuf
, sizeof (abuf
));
237 (void) strncpy(hostname
, abuf
, sizeof (hostname
));
246 if (read(f
, &c
, 1) != 1)
250 port
= port
* 10 + c
- '0';
254 s
= socket(fromp
->ss_family
, SOCK_STREAM
, 0);
257 if (bind(s
, (struct sockaddr
*)&asin
, fromplen
) < 0)
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
);
265 if (connect(s
, (struct sockaddr
*)fromp
, fromplen
) < 0)
269 getstr(user
, sizeof (user
), "username");
270 getstr(pass
, sizeof (pass
), "password");
271 getstr(cmdbuf
, sizeof (cmdbuf
), "command");
273 pwd
= getpwnam_r(user
, &pw_data
, pwdbuf
, sizeof (pwdbuf
));
275 (void) audit_rexecd_fail("Login incorrect", hostname
, user
,
277 error("Login incorrect.\n");
281 if (defopen(_PATH_DEFAULT_LOGIN
) == 0) {
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
;
294 if (pam_start("rexec", user
, &conv
, &pamh
) != PAM_SUCCESS
) {
297 if (pam_set_item(pamh
, PAM_RHOST
, hostname
) != PAM_SUCCESS
) {
301 if ((status
= pam_authenticate(pamh
, pam_flags
)) != PAM_SUCCESS
) {
303 case PAM_USER_UNKNOWN
:
304 (void) audit_rexecd_fail("Login incorrect", hostname
,
305 user
, cmdbuf
); /* BSM */
306 error("Login incorrect.\n");
309 (void) audit_rexecd_fail("Password incorrect", hostname
,
310 user
, cmdbuf
); /* BSM */
311 error("Password incorrect.\n");
313 pam_end(pamh
, status
);
316 if ((status
= pam_acct_mgmt(pamh
, pam_flags
)) != PAM_SUCCESS
) {
317 (void) audit_rexecd_fail("Account or Password Expired",
318 hostname
, user
, cmdbuf
);
320 case PAM_NEW_AUTHTOK_REQD
:
321 error("Password Expired.\n");
323 case PAM_PERM_DENIED
:
324 error("Account Expired.\n");
326 case PAM_AUTHTOK_EXPIRED
:
327 error("Password Expired.\n");
330 error("Login incorrect.\n");
333 pam_end(pamh
, status
);
337 (void) write(2, "\0", 1);
339 if (setgid((gid_t
)pwd
->pw_gid
) < 0) {
340 (void) audit_rexecd_fail("Can't setgid", hostname
,
341 user
, cmdbuf
); /* BSM */
343 pam_end(pamh
, PAM_ABORT
);
346 (void) initgroups(pwd
->pw_name
, pwd
->pw_gid
);
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
);
355 (void) audit_rexecd_success(hostname
, user
, cmdbuf
); /* BSM */
357 if (setuid((uid_t
)pwd
->pw_uid
) < 0) {
358 (void) audit_rexecd_fail("Can't setuid", hostname
,
359 user
, cmdbuf
); /* BSM */
361 pam_end(pamh
, PAM_ABORT
);
369 if (pid
== (pid_t
)-1) {
370 error("Try again.\n");
371 pam_end(pamh
, PAM_ABORT
);
376 * since the daemon is running as the user no need
377 * to prune privileges.
379 (void) close(0); (void) close(1); (void) close(2);
380 (void) close(f
); (void) close(pv
[1]);
382 FD_SET(s
, &readfrom
);
383 FD_SET(pv
[0], &readfrom
);
384 (void) ioctl(pv
[0], FIONBIO
, (char *)&one
);
385 /* should set s nbio! */
388 if (select(MAXFD(s
, pv
[0])+1, &ready
, NULL
,
393 if (FD_ISSET(s
, &ready
)) {
394 if (read(s
, &sig
, 1) <= 0)
395 FD_CLR(s
, &readfrom
);
397 (void) killpg(pid
, sig
);
399 if (FD_ISSET(pv
[0], &ready
)) {
400 cc
= read(pv
[0], buf
, sizeof (buf
));
402 (void) shutdown(s
, 1+1);
403 FD_CLR(pv
[0], &readfrom
);
405 (void) write(s
, buf
, cc
);
407 } while (FD_ISSET(s
, &readfrom
) ||
408 FD_ISSET(pv
[0], &readfrom
));
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);
417 if (*pwd
->pw_shell
== '\0')
418 pwd
->pw_shell
= "/bin/sh";
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
);
429 envinit
[ENVINIT_PATH
] = userpath
;
431 envinit
[ENVINIT_PATH
] = rootpath
;
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);
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
442 for (end_env
= 0; envinit
[end_env
] != 0; end_env
++)
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
];
454 pam_end(pamh
, PAM_SUCCESS
);
456 cp
= rindex(pwd
->pw_shell
, '/');
461 (void) execle(pwd
->pw_shell
, cp
, "-c", cmdbuf
, (char *)0, envinit
);
462 perror(pwd
->pw_shell
);
467 getstr(char *buf
, int cnt
, char *err
)
472 if (read(0, &c
, 1) != 1)
476 error("%s too long\n", err
);
483 error(char *fmt
, ...)
490 (void) vsprintf(buf
+1, fmt
, ap
);
492 (void) write(2, buf
, strlen(buf
));
495 static char *illegal
[] = {
510 * legalenvvar - can PAM insert this environmental variable?
518 for (p
= illegal
; *p
; p
++)
519 if (strncmp(s
, *p
, strlen(*p
)) == 0)
522 if (s
[0] == 'L' && s
[1] == 'D' && s
[2] == '_')
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.
536 rexec_conv(int num_msg
, struct pam_message
**msg
,
537 struct pam_response
**response
, void *appdata_ptr
)
539 struct pam_message
*m
;
540 struct pam_response
*r
;
544 return (PAM_CONV_ERR
);
546 *response
= calloc(num_msg
, sizeof (struct pam_response
));
547 if (*response
== NULL
)
548 return (PAM_BUF_ERR
);
553 if (m
->msg_style
== PAM_PROMPT_ECHO_OFF
) {
554 if (pass
[0] != '\0') {
555 r
->resp
= strdup(pass
);
556 if (r
->resp
== NULL
) {
559 for (i
= 0; i
< num_msg
; i
++, r
++) {
565 return (PAM_BUF_ERR
);
570 return (PAM_SUCCESS
);