| @@ -1,799 +1,800 @@ | | | @@ -1,799 +1,800 @@ |
1 | /* $NetBSD: rshd.c,v 1.46 2008/07/20 01:09:07 lukem Exp $ */ | | 1 | /* $NetBSD: rshd.c,v 1.47 2009/03/16 02:20:02 lukem Exp $ */ |
2 | | | 2 | |
3 | /* | | 3 | /* |
4 | * Copyright (C) 1998 WIDE Project. | | 4 | * Copyright (C) 1998 WIDE Project. |
5 | * All rights reserved. | | 5 | * All rights reserved. |
6 | * | | 6 | * |
7 | * Redistribution and use in source and binary forms, with or without | | 7 | * Redistribution and use in source and binary forms, with or without |
8 | * modification, are permitted provided that the following conditions | | 8 | * modification, are permitted provided that the following conditions |
9 | * are met: | | 9 | * are met: |
10 | * 1. Redistributions of source code must retain the above copyright | | 10 | * 1. Redistributions of source code must retain the above copyright |
11 | * notice, this list of conditions and the following disclaimer. | | 11 | * notice, this list of conditions and the following disclaimer. |
12 | * 2. Redistributions in binary form must reproduce the above copyright | | 12 | * 2. Redistributions in binary form must reproduce the above copyright |
13 | * notice, this list of conditions and the following disclaimer in the | | 13 | * notice, this list of conditions and the following disclaimer in the |
14 | * documentation and/or other materials provided with the distribution. | | 14 | * documentation and/or other materials provided with the distribution. |
15 | * 3. All advertising materials mentioning features or use of this software | | 15 | * 3. All advertising materials mentioning features or use of this software |
16 | * must display the following acknowledgement: | | 16 | * must display the following acknowledgement: |
17 | * This product includes software developed by WIDE Project and | | 17 | * This product includes software developed by WIDE Project and |
18 | * its contributors. | | 18 | * its contributors. |
19 | * 4. Neither the name of the project nor the names of its contributors | | 19 | * 4. Neither the name of the project nor the names of its contributors |
20 | * may be used to endorse or promote products derived from this software | | 20 | * may be used to endorse or promote products derived from this software |
21 | * without specific prior written permission. | | 21 | * without specific prior written permission. |
22 | * | | 22 | * |
23 | * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND | | 23 | * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND |
24 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | 24 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
25 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | 25 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
26 | * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE | | 26 | * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE |
27 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | 27 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
28 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | | 28 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
29 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | | 29 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
30 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | 30 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
31 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | 31 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
32 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | | 32 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
33 | * SUCH DAMAGE. | | 33 | * SUCH DAMAGE. |
34 | */ | | 34 | */ |
35 | | | 35 | |
36 | /*- | | 36 | /*- |
37 | * Copyright (c) 1988, 1989, 1992, 1993, 1994 | | 37 | * Copyright (c) 1988, 1989, 1992, 1993, 1994 |
38 | * The Regents of the University of California. All rights reserved. | | 38 | * The Regents of the University of California. All rights reserved. |
39 | * | | 39 | * |
40 | * Redistribution and use in source and binary forms, with or without | | 40 | * Redistribution and use in source and binary forms, with or without |
41 | * modification, are permitted provided that the following conditions | | 41 | * modification, are permitted provided that the following conditions |
42 | * are met: | | 42 | * are met: |
43 | * 1. Redistributions of source code must retain the above copyright | | 43 | * 1. Redistributions of source code must retain the above copyright |
44 | * notice, this list of conditions and the following disclaimer. | | 44 | * notice, this list of conditions and the following disclaimer. |
45 | * 2. Redistributions in binary form must reproduce the above copyright | | 45 | * 2. Redistributions in binary form must reproduce the above copyright |
46 | * notice, this list of conditions and the following disclaimer in the | | 46 | * notice, this list of conditions and the following disclaimer in the |
47 | * documentation and/or other materials provided with the distribution. | | 47 | * documentation and/or other materials provided with the distribution. |
48 | * 3. Neither the name of the University nor the names of its contributors | | 48 | * 3. Neither the name of the University nor the names of its contributors |
49 | * may be used to endorse or promote products derived from this software | | 49 | * may be used to endorse or promote products derived from this software |
50 | * without specific prior written permission. | | 50 | * without specific prior written permission. |
51 | * | | 51 | * |
52 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | | 52 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
53 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | 53 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
54 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | 54 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
55 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | | 55 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
56 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | 56 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
57 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | | 57 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
58 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | | 58 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
59 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | 59 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
60 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | 60 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
61 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | | 61 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
62 | * SUCH DAMAGE. | | 62 | * SUCH DAMAGE. |
63 | */ | | 63 | */ |
64 | | | 64 | |
65 | #include <sys/cdefs.h> | | 65 | #include <sys/cdefs.h> |
66 | #ifndef lint | | 66 | #ifndef lint |
67 | __COPYRIGHT("@(#) Copyright (c) 1988, 1989, 1992, 1993, 1994\ | | 67 | __COPYRIGHT("@(#) Copyright (c) 1988, 1989, 1992, 1993, 1994\ |
68 | The Regents of the University of California. All rights reserved."); | | 68 | The Regents of the University of California. All rights reserved."); |
69 | #if 0 | | 69 | #if 0 |
70 | static char sccsid[] = "@(#)rshd.c 8.2 (Berkeley) 4/6/94"; | | 70 | static char sccsid[] = "@(#)rshd.c 8.2 (Berkeley) 4/6/94"; |
71 | #else | | 71 | #else |
72 | __RCSID("$NetBSD: rshd.c,v 1.46 2008/07/20 01:09:07 lukem Exp $"); | | 72 | __RCSID("$NetBSD: rshd.c,v 1.47 2009/03/16 02:20:02 lukem Exp $"); |
73 | #endif | | 73 | #endif |
74 | #endif /* not lint */ | | 74 | #endif /* not lint */ |
75 | | | 75 | |
76 | /* | | 76 | /* |
77 | * remote shell server: | | 77 | * remote shell server: |
78 | * [port]\0 | | 78 | * [port]\0 |
79 | * remuser\0 | | 79 | * remuser\0 |
80 | * locuser\0 | | 80 | * locuser\0 |
81 | * command\0 | | 81 | * command\0 |
82 | * data | | 82 | * data |
83 | */ | | 83 | */ |
84 | #include <sys/param.h> | | 84 | #include <sys/param.h> |
85 | #include <sys/ioctl.h> | | 85 | #include <sys/ioctl.h> |
86 | #include <sys/time.h> | | 86 | #include <sys/time.h> |
87 | #include <sys/socket.h> | | 87 | #include <sys/socket.h> |
88 | | | 88 | |
89 | #include <netinet/in_systm.h> | | 89 | #include <netinet/in_systm.h> |
90 | #include <netinet/in.h> | | 90 | #include <netinet/in.h> |
91 | #include <netinet/ip.h> | | 91 | #include <netinet/ip.h> |
92 | #include <netinet/tcp.h> | | 92 | #include <netinet/tcp.h> |
93 | #include <arpa/inet.h> | | 93 | #include <arpa/inet.h> |
94 | #include <netdb.h> | | 94 | #include <netdb.h> |
95 | | | 95 | |
96 | #include <errno.h> | | 96 | #include <errno.h> |
97 | #include <fcntl.h> | | 97 | #include <fcntl.h> |
98 | #include <paths.h> | | 98 | #include <paths.h> |
99 | #include <pwd.h> | | 99 | #include <pwd.h> |
100 | #include <signal.h> | | 100 | #include <signal.h> |
101 | #include <stdio.h> | | 101 | #include <stdio.h> |
102 | #include <stdlib.h> | | 102 | #include <stdlib.h> |
103 | #include <string.h> | | 103 | #include <string.h> |
104 | #include <syslog.h> | | 104 | #include <syslog.h> |
105 | #include <unistd.h> | | 105 | #include <unistd.h> |
106 | #include <poll.h> | | 106 | #include <poll.h> |
107 | #ifdef LOGIN_CAP | | 107 | #ifdef LOGIN_CAP |
108 | #include <login_cap.h> | | 108 | #include <login_cap.h> |
109 | #endif | | 109 | #endif |
110 | | | 110 | |
111 | #ifdef USE_PAM | | 111 | #ifdef USE_PAM |
112 | #include <security/pam_appl.h> | | 112 | #include <security/pam_appl.h> |
113 | #include <security/openpam.h> | | 113 | #include <security/openpam.h> |
114 | #include <sys/wait.h> | | 114 | #include <sys/wait.h> |
115 | | | 115 | |
116 | static struct pam_conv pamc = { openpam_nullconv, NULL }; | | 116 | static struct pam_conv pamc = { openpam_nullconv, NULL }; |
117 | static pam_handle_t *pamh; | | 117 | static pam_handle_t *pamh; |
118 | static int pam_err; | | 118 | static int pam_err; |
119 | | | 119 | |
120 | #define PAM_END do { \ | | 120 | #define PAM_END do { \ |
121 | if ((pam_err = pam_setcred(pamh, PAM_DELETE_CRED)) != PAM_SUCCESS) \ | | 121 | if ((pam_err = pam_setcred(pamh, PAM_DELETE_CRED)) != PAM_SUCCESS) \ |
122 | syslog(LOG_ERR|LOG_AUTH, "pam_setcred(): %s", \ | | 122 | syslog(LOG_ERR|LOG_AUTH, "pam_setcred(): %s", \ |
123 | pam_strerror(pamh, pam_err)); \ | | 123 | pam_strerror(pamh, pam_err)); \ |
124 | if ((pam_err = pam_close_session(pamh,0)) != PAM_SUCCESS) \ | | 124 | if ((pam_err = pam_close_session(pamh,0)) != PAM_SUCCESS) \ |
125 | syslog(LOG_ERR|LOG_AUTH, "pam_close_session(): %s", \ | | 125 | syslog(LOG_ERR|LOG_AUTH, "pam_close_session(): %s", \ |
126 | pam_strerror(pamh, pam_err)); \ | | 126 | pam_strerror(pamh, pam_err)); \ |
127 | if ((pam_err = pam_end(pamh, pam_err)) != PAM_SUCCESS) \ | | 127 | if ((pam_err = pam_end(pamh, pam_err)) != PAM_SUCCESS) \ |
128 | syslog(LOG_ERR|LOG_AUTH, "pam_end(): %s", \ | | 128 | syslog(LOG_ERR|LOG_AUTH, "pam_end(): %s", \ |
129 | pam_strerror(pamh, pam_err)); \ | | 129 | pam_strerror(pamh, pam_err)); \ |
130 | } while (/*CONSTCOND*/0) | | 130 | } while (/*CONSTCOND*/0) |
131 | #else | | 131 | #else |
132 | #define PAM_END | | 132 | #define PAM_END |
133 | #endif | | 133 | #endif |
134 | | | 134 | |
135 | int keepalive = 1; | | 135 | int keepalive = 1; |
136 | int check_all; | | 136 | int check_all; |
137 | int log_success; /* If TRUE, log all successful accesses */ | | 137 | int log_success; /* If TRUE, log all successful accesses */ |
138 | int sent_null; | | 138 | int sent_null; |
139 | | | 139 | |
140 | void doit(struct sockaddr *) __dead; | | 140 | void doit(struct sockaddr *) __dead; |
141 | void rshd_errx(int, const char *, ...) | | 141 | void rshd_errx(int, const char *, ...) |
142 | __attribute__((__noreturn__, __format__(__printf__, 2, 3))); | | 142 | __attribute__((__noreturn__, __format__(__printf__, 2, 3))); |
143 | void getstr(char *, int, const char *); | | 143 | void getstr(char *, int, const char *); |
144 | int local_domain(char *); | | 144 | int local_domain(char *); |
145 | char *topdomain(char *); | | 145 | char *topdomain(char *); |
146 | void usage(void); | | 146 | void usage(void); |
147 | int main(int, char *[]); | | 147 | int main(int, char *[]); |
148 | | | 148 | |
149 | #define OPTIONS "aLln" | | 149 | #define OPTIONS "aLln" |
150 | extern int __check_rhosts_file; | | 150 | extern int __check_rhosts_file; |
151 | extern char *__rcmd_errstr; /* syslog hook from libc/net/rcmd.c. */ | | 151 | extern char *__rcmd_errstr; /* syslog hook from libc/net/rcmd.c. */ |
152 | static const char incorrect[] = "Login incorrect."; | | 152 | static const char incorrect[] = "Login incorrect."; |
153 | | | 153 | |
154 | int | | 154 | int |
155 | main(int argc, char *argv[]) | | 155 | main(int argc, char *argv[]) |
156 | { | | 156 | { |
157 | struct linger linger; | | 157 | struct linger linger; |
158 | int ch, on = 1; | | 158 | int ch, on = 1; |
159 | socklen_t fromlen; | | 159 | socklen_t fromlen; |
160 | struct sockaddr_storage from; | | 160 | struct sockaddr_storage from; |
161 | struct protoent *proto; | | 161 | struct protoent *proto; |
162 | | | 162 | |
163 | openlog("rshd", LOG_PID, LOG_DAEMON); | | 163 | openlog("rshd", LOG_PID, LOG_DAEMON); |
164 | | | 164 | |
165 | opterr = 0; | | 165 | opterr = 0; |
166 | while ((ch = getopt(argc, argv, OPTIONS)) != -1) | | 166 | while ((ch = getopt(argc, argv, OPTIONS)) != -1) |
167 | switch (ch) { | | 167 | switch (ch) { |
168 | case 'a': | | 168 | case 'a': |
169 | check_all = 1; | | 169 | check_all = 1; |
170 | break; | | 170 | break; |
171 | case 'l': | | 171 | case 'l': |
172 | __check_rhosts_file = 0; | | 172 | __check_rhosts_file = 0; |
173 | break; | | 173 | break; |
174 | case 'n': | | 174 | case 'n': |
175 | keepalive = 0; | | 175 | keepalive = 0; |
176 | break; | | 176 | break; |
177 | case 'L': | | 177 | case 'L': |
178 | log_success = 1; | | 178 | log_success = 1; |
179 | break; | | 179 | break; |
180 | case '?': | | 180 | case '?': |
181 | default: | | 181 | default: |
182 | usage(); | | 182 | usage(); |
183 | break; | | 183 | break; |
184 | } | | 184 | } |
185 | | | 185 | |
186 | argc -= optind; | | 186 | argc -= optind; |
187 | argv += optind; | | 187 | argv += optind; |
188 | | | 188 | |
189 | fromlen = sizeof(from); /* xxx */ | | 189 | fromlen = sizeof(from); /* xxx */ |
190 | if (getpeername(STDIN_FILENO, (struct sockaddr *)&from, &fromlen) < 0) { | | 190 | if (getpeername(STDIN_FILENO, (struct sockaddr *)&from, &fromlen) < 0) { |
191 | syslog(LOG_ERR, "getpeername: %m"); | | 191 | syslog(LOG_ERR, "getpeername: %m"); |
192 | return EXIT_FAILURE; | | 192 | return EXIT_FAILURE; |
193 | } | | 193 | } |
194 | #if 0 | | 194 | #if 0 |
195 | if (((struct sockaddr *)&from)->sa_family == AF_INET6 && | | 195 | if (((struct sockaddr *)&from)->sa_family == AF_INET6 && |
196 | IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)&from)->sin6_addr) && | | 196 | IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)&from)->sin6_addr) && |
197 | sizeof(struct sockaddr_in) <= sizeof(from)) { | | 197 | sizeof(struct sockaddr_in) <= sizeof(from)) { |
198 | struct sockaddr_in sin; | | 198 | struct sockaddr_in sin; |
199 | struct sockaddr_in6 *sin6; | | 199 | struct sockaddr_in6 *sin6; |
200 | const int off = sizeof(struct sockaddr_in6) - | | 200 | const int off = sizeof(struct sockaddr_in6) - |
201 | sizeof(struct sockaddr_in); | | 201 | sizeof(struct sockaddr_in); |
202 | | | 202 | |
203 | sin6 = (struct sockaddr_in6 *)&from; | | 203 | sin6 = (struct sockaddr_in6 *)&from; |
204 | (void)memset(&sin, 0, sizeof(sin)); | | 204 | (void)memset(&sin, 0, sizeof(sin)); |
205 | sin.sin_family = AF_INET; | | 205 | sin.sin_family = AF_INET; |
206 | sin.sin_len = sizeof(struct sockaddr_in); | | 206 | sin.sin_len = sizeof(struct sockaddr_in); |
207 | (void)memcpy(&sin.sin_addr, &sin6->sin6_addr.s6_addr[off], | | 207 | (void)memcpy(&sin.sin_addr, &sin6->sin6_addr.s6_addr[off], |
208 | sizeof(sin.sin_addr)); | | 208 | sizeof(sin.sin_addr)); |
209 | (void)memcpy(&from, &sin, sizeof(sin)); | | 209 | (void)memcpy(&from, &sin, sizeof(sin)); |
210 | fromlen = sin.sin_len; | | 210 | fromlen = sin.sin_len; |
211 | } | | 211 | } |
212 | #else | | 212 | #else |
213 | if (((struct sockaddr *)&from)->sa_family == AF_INET6 && | | 213 | if (((struct sockaddr *)&from)->sa_family == AF_INET6 && |
214 | IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)&from)->sin6_addr)) { | | 214 | IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)&from)->sin6_addr)) { |
215 | char hbuf[NI_MAXHOST]; | | 215 | char hbuf[NI_MAXHOST]; |
216 | if (getnameinfo((struct sockaddr *)&from, fromlen, hbuf, | | 216 | if (getnameinfo((struct sockaddr *)&from, fromlen, hbuf, |
217 | sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0) { | | 217 | sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0) { |
218 | strlcpy(hbuf, "invalid", sizeof(hbuf)); | | 218 | strlcpy(hbuf, "invalid", sizeof(hbuf)); |
219 | } | | 219 | } |
220 | syslog(LOG_ERR, "malformed \"from\" address (v4 mapped, %s)", | | 220 | syslog(LOG_ERR, "malformed \"from\" address (v4 mapped, %s)", |
221 | hbuf); | | 221 | hbuf); |
222 | return EXIT_FAILURE; | | 222 | return EXIT_FAILURE; |
223 | } | | 223 | } |
224 | #endif | | 224 | #endif |
225 | if (keepalive && | | 225 | if (keepalive && |
226 | setsockopt(STDIN_FILENO, SOL_SOCKET, SO_KEEPALIVE, (char *)&on, | | 226 | setsockopt(STDIN_FILENO, SOL_SOCKET, SO_KEEPALIVE, (char *)&on, |
227 | sizeof(on)) < 0) | | 227 | sizeof(on)) < 0) |
228 | syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); | | 228 | syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); |
229 | linger.l_onoff = 1; | | 229 | linger.l_onoff = 1; |
230 | linger.l_linger = 60; /* XXX */ | | 230 | linger.l_linger = 60; /* XXX */ |
231 | if (setsockopt(STDIN_FILENO, SOL_SOCKET, SO_LINGER, (char *)&linger, | | 231 | if (setsockopt(STDIN_FILENO, SOL_SOCKET, SO_LINGER, (char *)&linger, |
232 | sizeof (linger)) < 0) | | 232 | sizeof (linger)) < 0) |
233 | syslog(LOG_WARNING, "setsockopt (SO_LINGER): %m"); | | 233 | syslog(LOG_WARNING, "setsockopt (SO_LINGER): %m"); |
234 | proto = getprotobyname("tcp"); | | 234 | proto = getprotobyname("tcp"); |
235 | (void)setsockopt(STDIN_FILENO, proto->p_proto, TCP_NODELAY, &on, | | 235 | (void)setsockopt(STDIN_FILENO, proto->p_proto, TCP_NODELAY, &on, |
236 | sizeof(on)); | | 236 | sizeof(on)); |
237 | doit((struct sockaddr *)&from); | | 237 | doit((struct sockaddr *)&from); |
238 | } | | 238 | } |
239 | | | 239 | |
240 | char username[20] = "USER="; | | 240 | char username[20] = "USER="; |
241 | char homedir[64] = "HOME="; | | 241 | char homedir[64] = "HOME="; |
242 | char shell[64] = "SHELL="; | | 242 | char shell[64] = "SHELL="; |
243 | char path[100] = "PATH="; | | 243 | char path[100] = "PATH="; |
244 | char *envinit[] = | | 244 | char *envinit[] = |
245 | {homedir, shell, path, username, 0}; | | 245 | {homedir, shell, path, username, 0}; |
246 | char **environ; | | 246 | char **environ; |
247 | | | 247 | |
248 | void | | 248 | void |
249 | doit(struct sockaddr *fromp) | | 249 | doit(struct sockaddr *fromp) |
250 | { | | 250 | { |
251 | struct passwd *pwd, pwres; | | 251 | struct passwd *pwd, pwres; |
252 | in_port_t port; | | 252 | in_port_t port; |
253 | struct pollfd set[2]; | | 253 | struct pollfd set[2]; |
254 | int cc, pv[2], pid, s = -1; /* XXX gcc */ | | 254 | int cc, pv[2], pid, s = -1; /* XXX gcc */ |
255 | int one = 1; | | 255 | int one = 1; |
256 | char *hostname, *errorhost = NULL; /* XXX gcc */ | | 256 | char *hostname, *errorhost = NULL; /* XXX gcc */ |
257 | const char *cp; | | 257 | const char *cp; |
258 | char sig, buf[BUFSIZ]; | | 258 | char sig, buf[BUFSIZ]; |
259 | char cmdbuf[NCARGS+1], locuser[16], remuser[16]; | | 259 | char cmdbuf[NCARGS+1], locuser[16], remuser[16]; |
260 | char remotehost[2 * MAXHOSTNAMELEN + 1]; | | 260 | char remotehost[2 * MAXHOSTNAMELEN + 1]; |
261 | char hostnamebuf[2 * MAXHOSTNAMELEN + 1]; | | 261 | char hostnamebuf[2 * MAXHOSTNAMELEN + 1]; |
262 | #ifdef LOGIN_CAP | | 262 | #ifdef LOGIN_CAP |
263 | login_cap_t *lc; | | 263 | login_cap_t *lc; |
264 | #endif | | 264 | #endif |
265 | char naddr[NI_MAXHOST]; | | 265 | char naddr[NI_MAXHOST]; |
266 | char saddr[NI_MAXHOST]; | | 266 | char saddr[NI_MAXHOST]; |
267 | char raddr[NI_MAXHOST]; | | 267 | char raddr[NI_MAXHOST]; |
268 | char pbuf[NI_MAXSERV]; | | 268 | char pbuf[NI_MAXSERV]; |
269 | int af = fromp->sa_family; | | 269 | int af = fromp->sa_family; |
270 | u_int16_t *portp; | | 270 | u_int16_t *portp; |
271 | struct addrinfo hints, *res, *res0; | | 271 | struct addrinfo hints, *res, *res0; |
272 | int gaierror; | | 272 | int gaierror; |
273 | const int niflags = NI_NUMERICHOST | NI_NUMERICSERV; | | 273 | const int niflags = NI_NUMERICHOST | NI_NUMERICSERV; |
274 | const char *errormsg = NULL, *errorstr = NULL; | | 274 | const char *errormsg = NULL, *errorstr = NULL; |
275 | char pwbuf[1024]; | | 275 | char pwbuf[1024]; |
276 | | | 276 | |
277 | (void)signal(SIGINT, SIG_DFL); | | 277 | (void)signal(SIGINT, SIG_DFL); |
278 | (void)signal(SIGQUIT, SIG_DFL); | | 278 | (void)signal(SIGQUIT, SIG_DFL); |
279 | (void)signal(SIGTERM, SIG_DFL); | | 279 | (void)signal(SIGTERM, SIG_DFL); |
280 | #ifdef DEBUG | | 280 | #ifdef DEBUG |
281 | { | | 281 | { |
282 | int t = open(_PATH_TTY, O_RDWR); | | 282 | int t = open(_PATH_TTY, O_RDWR); |
283 | if (t >= 0) { | | 283 | if (t >= 0) { |
284 | ioctl(t, TIOCNOTTY, NULL); | | 284 | ioctl(t, TIOCNOTTY, NULL); |
285 | (void)close(t); | | 285 | (void)close(t); |
286 | } | | 286 | } |
287 | } | | 287 | } |
288 | #endif | | 288 | #endif |
289 | switch (af) { | | 289 | switch (af) { |
290 | case AF_INET: | | 290 | case AF_INET: |
291 | portp = &((struct sockaddr_in *)fromp)->sin_port; | | 291 | portp = &((struct sockaddr_in *)fromp)->sin_port; |
292 | break; | | 292 | break; |
293 | #ifdef INET6 | | 293 | #ifdef INET6 |
294 | case AF_INET6: | | 294 | case AF_INET6: |
295 | portp = &((struct sockaddr_in6 *)fromp)->sin6_port; | | 295 | portp = &((struct sockaddr_in6 *)fromp)->sin6_port; |
296 | break; | | 296 | break; |
297 | #endif | | 297 | #endif |
298 | default: | | 298 | default: |
299 | syslog(LOG_ERR, "malformed \"from\" address (af %d)", af); | | 299 | syslog(LOG_ERR, "malformed \"from\" address (af %d)", af); |
300 | exit(EXIT_FAILURE); | | 300 | exit(EXIT_FAILURE); |
301 | } | | 301 | } |
302 | if (getnameinfo(fromp, fromp->sa_len, naddr, sizeof(naddr), | | 302 | if (getnameinfo(fromp, fromp->sa_len, naddr, sizeof(naddr), |
303 | pbuf, sizeof(pbuf), niflags) != 0) { | | 303 | pbuf, sizeof(pbuf), niflags) != 0) { |
304 | syslog(LOG_ERR, "malformed \"from\" address (af %d)", af); | | 304 | syslog(LOG_ERR, "malformed \"from\" address (af %d)", af); |
305 | exit(EXIT_FAILURE); | | 305 | exit(EXIT_FAILURE); |
306 | } | | 306 | } |
307 | #ifdef IP_OPTIONS | | 307 | #ifdef IP_OPTIONS |
308 | if (af == AF_INET) { | | 308 | if (af == AF_INET) { |
309 | | | 309 | |
310 | u_char optbuf[BUFSIZ/3]; | | 310 | u_char optbuf[BUFSIZ/3]; |
311 | socklen_t optsize = sizeof(optbuf); | | 311 | socklen_t optsize = sizeof(optbuf); |
312 | int ipproto, i; | | 312 | int ipproto; |
| | | 313 | unsigned int i; |
313 | struct protoent *ip; | | 314 | struct protoent *ip; |
314 | | | 315 | |
315 | if ((ip = getprotobyname("ip")) != NULL) | | 316 | if ((ip = getprotobyname("ip")) != NULL) |
316 | ipproto = ip->p_proto; | | 317 | ipproto = ip->p_proto; |
317 | else | | 318 | else |
318 | ipproto = IPPROTO_IP; | | 319 | ipproto = IPPROTO_IP; |
319 | if (!getsockopt(0, ipproto, IP_OPTIONS, (char *)optbuf, &optsize) && | | 320 | if (!getsockopt(0, ipproto, IP_OPTIONS, (char *)optbuf, &optsize) && |
320 | optsize != 0) { | | 321 | optsize != 0) { |
321 | for (i = 0; i < optsize;) { | | 322 | for (i = 0; i < optsize;) { |
322 | u_char c = optbuf[i]; | | 323 | u_char c = optbuf[i]; |
323 | if (c == IPOPT_LSRR || c == IPOPT_SSRR) { | | 324 | if (c == IPOPT_LSRR || c == IPOPT_SSRR) { |
324 | syslog(LOG_NOTICE, | | 325 | syslog(LOG_NOTICE, |
325 | "Connection refused from %s " | | 326 | "Connection refused from %s " |
326 | "with IP option %s", | | 327 | "with IP option %s", |
327 | inet_ntoa(( | | 328 | inet_ntoa(( |
328 | (struct sockaddr_in *)fromp)->sin_addr), | | 329 | (struct sockaddr_in *)fromp)->sin_addr), |
329 | c == IPOPT_LSRR ? "LSRR" : "SSRR"); | | 330 | c == IPOPT_LSRR ? "LSRR" : "SSRR"); |
330 | exit(EXIT_FAILURE); | | 331 | exit(EXIT_FAILURE); |
331 | } | | 332 | } |
332 | if (c == IPOPT_EOL) | | 333 | if (c == IPOPT_EOL) |
333 | break; | | 334 | break; |
334 | i += (c == IPOPT_NOP) ? 1 : optbuf[i + 1]; | | 335 | i += (c == IPOPT_NOP) ? 1 : optbuf[i + 1]; |
335 | } | | 336 | } |
336 | } | | 337 | } |
337 | } | | 338 | } |
338 | #endif | | 339 | #endif |
339 | if (ntohs(*portp) >= IPPORT_RESERVED | | 340 | if (ntohs(*portp) >= IPPORT_RESERVED |
340 | || ntohs(*portp) < IPPORT_RESERVED / 2) { | | 341 | || ntohs(*portp) < IPPORT_RESERVED / 2) { |
341 | syslog(LOG_NOTICE|LOG_AUTH, | | 342 | syslog(LOG_NOTICE|LOG_AUTH, |
342 | "Connection from %s on illegal port %u", | | 343 | "Connection from %s on illegal port %u", |
343 | naddr, ntohs(*portp)); | | 344 | naddr, ntohs(*portp)); |
344 | exit(EXIT_FAILURE); | | 345 | exit(EXIT_FAILURE); |
345 | } | | 346 | } |
346 | | | 347 | |
347 | (void) alarm(60); | | 348 | (void) alarm(60); |
348 | port = 0; | | 349 | port = 0; |
349 | for (;;) { | | 350 | for (;;) { |
350 | char c; | | 351 | char c; |
351 | | | 352 | |
352 | if ((cc = read(STDIN_FILENO, &c, 1)) != 1) { | | 353 | if ((cc = read(STDIN_FILENO, &c, 1)) != 1) { |
353 | if (cc < 0) | | 354 | if (cc < 0) |
354 | syslog(LOG_ERR, "read: %m"); | | 355 | syslog(LOG_ERR, "read: %m"); |
355 | (void)shutdown(0, SHUT_RDWR); | | 356 | (void)shutdown(0, SHUT_RDWR); |
356 | exit(EXIT_FAILURE); | | 357 | exit(EXIT_FAILURE); |
357 | } | | 358 | } |
358 | if (c == 0) | | 359 | if (c == 0) |
359 | break; | | 360 | break; |
360 | port = port * 10 + c - '0'; | | 361 | port = port * 10 + c - '0'; |
361 | } | | 362 | } |
362 | | | 363 | |
363 | (void) alarm(0); | | 364 | (void) alarm(0); |
364 | if (port != 0) { | | 365 | if (port != 0) { |
365 | int lport = IPPORT_RESERVED - 1; | | 366 | int lport = IPPORT_RESERVED - 1; |
366 | s = rresvport_af(&lport, af); | | 367 | s = rresvport_af(&lport, af); |
367 | if (s < 0) { | | 368 | if (s < 0) { |
368 | syslog(LOG_ERR, "can't get stderr port: %m"); | | 369 | syslog(LOG_ERR, "can't get stderr port: %m"); |
369 | exit(EXIT_FAILURE); | | 370 | exit(EXIT_FAILURE); |
370 | } | | 371 | } |
371 | if (port >= IPPORT_RESERVED) { | | 372 | if (port >= IPPORT_RESERVED) { |
372 | syslog(LOG_ERR, "2nd port not reserved"); | | 373 | syslog(LOG_ERR, "2nd port not reserved"); |
373 | exit(EXIT_FAILURE); | | 374 | exit(EXIT_FAILURE); |
374 | } | | 375 | } |
375 | *portp = htons(port); | | 376 | *portp = htons(port); |
376 | if (connect(s, fromp, fromp->sa_len) < 0) { | | 377 | if (connect(s, fromp, fromp->sa_len) < 0) { |
377 | syslog(LOG_ERR, "connect second port %d: %m", port); | | 378 | syslog(LOG_ERR, "connect second port %d: %m", port); |
378 | exit(EXIT_FAILURE); | | 379 | exit(EXIT_FAILURE); |
379 | } | | 380 | } |
380 | } | | 381 | } |
381 | | | 382 | |
382 | | | 383 | |
383 | #ifdef notdef | | 384 | #ifdef notdef |
384 | /* from inetd, socket is already on 0, 1, 2 */ | | 385 | /* from inetd, socket is already on 0, 1, 2 */ |
385 | (void)dup2(f, STDIN_FILENO); | | 386 | (void)dup2(f, STDIN_FILENO); |
386 | (void)dup2(f, STDOUT_FILENO); | | 387 | (void)dup2(f, STDOUT_FILENO); |
387 | (void)dup2(f, STDERR_FILENO); | | 388 | (void)dup2(f, STDERR_FILENO); |
388 | #endif | | 389 | #endif |
389 | if (getnameinfo(fromp, fromp->sa_len, saddr, sizeof(saddr), | | 390 | if (getnameinfo(fromp, fromp->sa_len, saddr, sizeof(saddr), |
390 | NULL, 0, NI_NAMEREQD) == 0) { | | 391 | NULL, 0, NI_NAMEREQD) == 0) { |
391 | /* | | 392 | /* |
392 | * If name returned by getnameinfo is in our domain, | | 393 | * If name returned by getnameinfo is in our domain, |
393 | * attempt to verify that we haven't been fooled by someone | | 394 | * attempt to verify that we haven't been fooled by someone |
394 | * in a remote net; look up the name and check that this | | 395 | * in a remote net; look up the name and check that this |
395 | * address corresponds to the name. | | 396 | * address corresponds to the name. |
396 | */ | | 397 | */ |
397 | hostname = saddr; | | 398 | hostname = saddr; |
398 | res0 = NULL; | | 399 | res0 = NULL; |
399 | if (check_all || local_domain(saddr)) { | | 400 | if (check_all || local_domain(saddr)) { |
400 | (void)strlcpy(remotehost, saddr, sizeof(remotehost)); | | 401 | (void)strlcpy(remotehost, saddr, sizeof(remotehost)); |
401 | errorhost = remotehost; | | 402 | errorhost = remotehost; |
402 | (void)memset(&hints, 0, sizeof(hints)); | | 403 | (void)memset(&hints, 0, sizeof(hints)); |
403 | hints.ai_family = fromp->sa_family; | | 404 | hints.ai_family = fromp->sa_family; |
404 | hints.ai_socktype = SOCK_STREAM; | | 405 | hints.ai_socktype = SOCK_STREAM; |
405 | hints.ai_flags = AI_CANONNAME; | | 406 | hints.ai_flags = AI_CANONNAME; |
406 | gaierror = getaddrinfo(remotehost, pbuf, &hints, &res0); | | 407 | gaierror = getaddrinfo(remotehost, pbuf, &hints, &res0); |
407 | if (gaierror) { | | 408 | if (gaierror) { |
408 | syslog(LOG_NOTICE, | | 409 | syslog(LOG_NOTICE, |
409 | "Couldn't look up address for %s: %s", | | 410 | "Couldn't look up address for %s: %s", |
410 | remotehost, gai_strerror(gaierror)); | | 411 | remotehost, gai_strerror(gaierror)); |
411 | errorstr = | | 412 | errorstr = |
412 | "Couldn't look up address for your host (%s)\n"; | | 413 | "Couldn't look up address for your host (%s)\n"; |
413 | hostname = naddr; | | 414 | hostname = naddr; |
414 | } else { | | 415 | } else { |
415 | for (res = res0; res; res = res->ai_next) { | | 416 | for (res = res0; res; res = res->ai_next) { |
416 | if (res->ai_family != fromp->sa_family) | | 417 | if (res->ai_family != fromp->sa_family) |
417 | continue; | | 418 | continue; |
418 | if (res->ai_addrlen != fromp->sa_len) | | 419 | if (res->ai_addrlen != fromp->sa_len) |
419 | continue; | | 420 | continue; |
420 | if (getnameinfo(res->ai_addr, | | 421 | if (getnameinfo(res->ai_addr, |
421 | res->ai_addrlen, | | 422 | res->ai_addrlen, |
422 | raddr, sizeof(raddr), NULL, 0, | | 423 | raddr, sizeof(raddr), NULL, 0, |
423 | niflags) == 0 | | 424 | niflags) == 0 |
424 | && strcmp(naddr, raddr) == 0) { | | 425 | && strcmp(naddr, raddr) == 0) { |
425 | hostname = res->ai_canonname | | 426 | hostname = res->ai_canonname |
426 | ? res->ai_canonname | | 427 | ? res->ai_canonname |
427 | : saddr; | | 428 | : saddr; |
428 | break; | | 429 | break; |
429 | } | | 430 | } |
430 | } | | 431 | } |
431 | if (res == NULL) { | | 432 | if (res == NULL) { |
432 | syslog(LOG_NOTICE, | | 433 | syslog(LOG_NOTICE, |
433 | "Host addr %s not listed for host %s", | | 434 | "Host addr %s not listed for host %s", |
434 | naddr, res0->ai_canonname | | 435 | naddr, res0->ai_canonname |
435 | ? res0->ai_canonname | | 436 | ? res0->ai_canonname |
436 | : saddr); | | 437 | : saddr); |
437 | errorstr = | | 438 | errorstr = |
438 | "Host address mismatch for %s\n"; | | 439 | "Host address mismatch for %s\n"; |
439 | hostname = naddr; | | 440 | hostname = naddr; |
440 | } | | 441 | } |
441 | } | | 442 | } |
442 | } | | 443 | } |
443 | (void)strlcpy(hostnamebuf, hostname, sizeof(hostnamebuf)); | | 444 | (void)strlcpy(hostnamebuf, hostname, sizeof(hostnamebuf)); |
444 | hostname = hostnamebuf; | | 445 | hostname = hostnamebuf; |
445 | if (res0) | | 446 | if (res0) |
446 | freeaddrinfo(res0); | | 447 | freeaddrinfo(res0); |
447 | } else { | | 448 | } else { |
448 | (void)strlcpy(hostnamebuf, naddr, sizeof(hostnamebuf)); | | 449 | (void)strlcpy(hostnamebuf, naddr, sizeof(hostnamebuf)); |
449 | errorhost = hostname = hostnamebuf; | | 450 | errorhost = hostname = hostnamebuf; |
450 | } | | 451 | } |
451 | | | 452 | |
452 | (void)alarm(60); | | 453 | (void)alarm(60); |
453 | getstr(remuser, sizeof(remuser), "remuser"); | | 454 | getstr(remuser, sizeof(remuser), "remuser"); |
454 | getstr(locuser, sizeof(locuser), "locuser"); | | 455 | getstr(locuser, sizeof(locuser), "locuser"); |
455 | getstr(cmdbuf, sizeof(cmdbuf), "command"); | | 456 | getstr(cmdbuf, sizeof(cmdbuf), "command"); |
456 | (void)alarm(0); | | 457 | (void)alarm(0); |
457 | | | 458 | |
458 | #ifdef USE_PAM | | 459 | #ifdef USE_PAM |
459 | pam_err = pam_start("rsh", locuser, &pamc, &pamh); | | 460 | pam_err = pam_start("rsh", locuser, &pamc, &pamh); |
460 | if (pam_err != PAM_SUCCESS) { | | 461 | if (pam_err != PAM_SUCCESS) { |
461 | syslog(LOG_ERR|LOG_AUTH, "pam_start(): %s", | | 462 | syslog(LOG_ERR|LOG_AUTH, "pam_start(): %s", |
462 | pam_strerror(pamh, pam_err)); | | 463 | pam_strerror(pamh, pam_err)); |
463 | rshd_errx(EXIT_FAILURE, incorrect); | | 464 | rshd_errx(EXIT_FAILURE, incorrect); |
464 | } | | 465 | } |
465 | | | 466 | |
466 | if ((pam_err = pam_set_item(pamh, PAM_RUSER, remuser)) != PAM_SUCCESS || | | 467 | if ((pam_err = pam_set_item(pamh, PAM_RUSER, remuser)) != PAM_SUCCESS || |
467 | (pam_err = pam_set_item(pamh, PAM_RHOST, hostname) != PAM_SUCCESS)){ | | 468 | (pam_err = pam_set_item(pamh, PAM_RHOST, hostname) != PAM_SUCCESS)){ |
468 | syslog(LOG_ERR|LOG_AUTH, "pam_set_item(): %s", | | 469 | syslog(LOG_ERR|LOG_AUTH, "pam_set_item(): %s", |
469 | pam_strerror(pamh, pam_err)); | | 470 | pam_strerror(pamh, pam_err)); |
470 | rshd_errx(EXIT_FAILURE, incorrect); | | 471 | rshd_errx(EXIT_FAILURE, incorrect); |
471 | } | | 472 | } |
472 | | | 473 | |
473 | pam_err = pam_authenticate(pamh, 0); | | 474 | pam_err = pam_authenticate(pamh, 0); |
474 | if (pam_err == PAM_SUCCESS) { | | 475 | if (pam_err == PAM_SUCCESS) { |
475 | if ((pam_err = pam_get_user(pamh, &cp, NULL)) == PAM_SUCCESS) { | | 476 | if ((pam_err = pam_get_user(pamh, &cp, NULL)) == PAM_SUCCESS) { |
476 | (void)strlcpy(locuser, cp, sizeof(locuser)); | | 477 | (void)strlcpy(locuser, cp, sizeof(locuser)); |
477 | /* XXX truncation! */ | | 478 | /* XXX truncation! */ |
478 | } | | 479 | } |
479 | pam_err = pam_acct_mgmt(pamh, 0); | | 480 | pam_err = pam_acct_mgmt(pamh, 0); |
480 | } | | 481 | } |
481 | if (pam_err != PAM_SUCCESS) { | | 482 | if (pam_err != PAM_SUCCESS) { |
482 | errorstr = incorrect; | | 483 | errorstr = incorrect; |
483 | errormsg = pam_strerror(pamh, pam_err); | | 484 | errormsg = pam_strerror(pamh, pam_err); |
484 | goto badlogin; | | 485 | goto badlogin; |
485 | } | | 486 | } |
486 | #endif /* USE_PAM */ | | 487 | #endif /* USE_PAM */ |
487 | setpwent(); | | 488 | setpwent(); |
488 | if (getpwnam_r(locuser, &pwres, pwbuf, sizeof(pwbuf), &pwd) != 0 || | | 489 | if (getpwnam_r(locuser, &pwres, pwbuf, sizeof(pwbuf), &pwd) != 0 || |
489 | pwd == NULL) { | | 490 | pwd == NULL) { |
490 | syslog(LOG_INFO|LOG_AUTH, | | 491 | syslog(LOG_INFO|LOG_AUTH, |
491 | "%s@%s as %s: unknown login. cmd='%.80s'", | | 492 | "%s@%s as %s: unknown login. cmd='%.80s'", |
492 | remuser, hostname, locuser, cmdbuf); | | 493 | remuser, hostname, locuser, cmdbuf); |
493 | if (errorstr == NULL) | | 494 | if (errorstr == NULL) |
494 | errorstr = "Permission denied."; | | 495 | errorstr = "Permission denied."; |
495 | rshd_errx(EXIT_FAILURE, errorstr, errorhost); | | 496 | rshd_errx(EXIT_FAILURE, errorstr, errorhost); |
496 | } | | 497 | } |
497 | #ifdef LOGIN_CAP | | 498 | #ifdef LOGIN_CAP |
498 | lc = login_getclass(pwd ? pwd->pw_class : NULL); | | 499 | lc = login_getclass(pwd ? pwd->pw_class : NULL); |
499 | #endif | | 500 | #endif |
500 | | | 501 | |
501 | if (chdir(pwd->pw_dir) < 0) { | | 502 | if (chdir(pwd->pw_dir) < 0) { |
502 | if (chdir("/") < 0 | | 503 | if (chdir("/") < 0 |
503 | #ifdef LOGIN_CAP | | 504 | #ifdef LOGIN_CAP |
504 | || login_getcapbool(lc, "requirehome", pwd->pw_uid ? 1 : 0) | | 505 | || login_getcapbool(lc, "requirehome", pwd->pw_uid ? 1 : 0) |
505 | #endif | | 506 | #endif |
506 | ) { | | 507 | ) { |
507 | syslog(LOG_INFO|LOG_AUTH, | | 508 | syslog(LOG_INFO|LOG_AUTH, |
508 | "%s@%s as %s: no home directory. cmd='%.80s'", | | 509 | "%s@%s as %s: no home directory. cmd='%.80s'", |
509 | remuser, hostname, locuser, cmdbuf); | | 510 | remuser, hostname, locuser, cmdbuf); |
510 | rshd_errx(EXIT_SUCCESS, "No remote home directory."); | | 511 | rshd_errx(EXIT_SUCCESS, "No remote home directory."); |
511 | } | | 512 | } |
512 | } | | 513 | } |
513 | | | 514 | |
514 | #ifndef USE_PAM | | 515 | #ifndef USE_PAM |
515 | if (errorstr || | | 516 | if (errorstr || |
516 | (pwd->pw_passwd != 0 && *pwd->pw_passwd != '\0' && | | 517 | (pwd->pw_passwd != 0 && *pwd->pw_passwd != '\0' && |
517 | iruserok_sa(fromp, fromp->sa_len, pwd->pw_uid == 0, remuser, | | 518 | iruserok_sa(fromp, fromp->sa_len, pwd->pw_uid == 0, remuser, |
518 | locuser) < 0)) { | | 519 | locuser) < 0)) { |
519 | errormsg = __rcmd_errstr ? __rcmd_errstr : "unknown error"; | | 520 | errormsg = __rcmd_errstr ? __rcmd_errstr : "unknown error"; |
520 | if (errorstr == NULL) | | 521 | if (errorstr == NULL) |
521 | errorstr = "Permission denied."; | | 522 | errorstr = "Permission denied."; |
522 | goto badlogin; | | 523 | goto badlogin; |
523 | } | | 524 | } |
524 | | | 525 | |
525 | if (pwd->pw_uid && !access(_PATH_NOLOGIN, F_OK)) | | 526 | if (pwd->pw_uid && !access(_PATH_NOLOGIN, F_OK)) |
526 | rshd_errx(EXIT_FAILURE, "Logins currently disabled."); | | 527 | rshd_errx(EXIT_FAILURE, "Logins currently disabled."); |
527 | #endif | | 528 | #endif |
528 | | | 529 | |
529 | #ifdef LOGIN_CAP | | 530 | #ifdef LOGIN_CAP |
530 | /* | | 531 | /* |
531 | * PAM modules might add supplementary groups in | | 532 | * PAM modules might add supplementary groups in |
532 | * pam_setcred(), so initialize them first. | | 533 | * pam_setcred(), so initialize them first. |
533 | * But we need to open the session as root. | | 534 | * But we need to open the session as root. |
534 | */ | | 535 | */ |
535 | if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) != 0) { | | 536 | if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) != 0) { |
536 | syslog(LOG_ERR, "setusercontext: %m"); | | 537 | syslog(LOG_ERR, "setusercontext: %m"); |
537 | exit(EXIT_FAILURE); | | 538 | exit(EXIT_FAILURE); |
538 | } | | 539 | } |
539 | #else | | 540 | #else |
540 | initgroups(pwd->pw_name, pwd->pw_gid); | | 541 | initgroups(pwd->pw_name, pwd->pw_gid); |
541 | #endif | | 542 | #endif |
542 | | | 543 | |
543 | #ifdef USE_PAM | | 544 | #ifdef USE_PAM |
544 | if ((pam_err = pam_open_session(pamh, 0)) != PAM_SUCCESS) { | | 545 | if ((pam_err = pam_open_session(pamh, 0)) != PAM_SUCCESS) { |
545 | syslog(LOG_ERR, "pam_open_session: %s", | | 546 | syslog(LOG_ERR, "pam_open_session: %s", |
546 | pam_strerror(pamh, pam_err)); | | 547 | pam_strerror(pamh, pam_err)); |
547 | } else if ((pam_err = pam_setcred(pamh, PAM_ESTABLISH_CRED)) | | 548 | } else if ((pam_err = pam_setcred(pamh, PAM_ESTABLISH_CRED)) |
548 | != PAM_SUCCESS) { | | 549 | != PAM_SUCCESS) { |
549 | syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, pam_err)); | | 550 | syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, pam_err)); |
550 | } | | 551 | } |
551 | #endif | | 552 | #endif |
552 | | | 553 | |
553 | (void)write(STDERR_FILENO, "\0", 1); | | 554 | (void)write(STDERR_FILENO, "\0", 1); |
554 | sent_null = 1; | | 555 | sent_null = 1; |
555 | | | 556 | |
556 | if (port) { | | 557 | if (port) { |
557 | if (pipe(pv) < 0) | | 558 | if (pipe(pv) < 0) |
558 | rshd_errx(EXIT_FAILURE, "Can't make pipe. (%s)", | | 559 | rshd_errx(EXIT_FAILURE, "Can't make pipe. (%s)", |
559 | strerror(errno)); | | 560 | strerror(errno)); |
560 | pid = fork(); | | 561 | pid = fork(); |
561 | if (pid == -1) | | 562 | if (pid == -1) |
562 | rshd_errx(EXIT_FAILURE, "Can't fork. (%s)", | | 563 | rshd_errx(EXIT_FAILURE, "Can't fork. (%s)", |
563 | strerror(errno)); | | 564 | strerror(errno)); |
564 | if (pid) { | | 565 | if (pid) { |
565 | (void)close(STDIN_FILENO); | | 566 | (void)close(STDIN_FILENO); |
566 | (void)close(STDOUT_FILENO); | | 567 | (void)close(STDOUT_FILENO); |
567 | (void)close(STDERR_FILENO); | | 568 | (void)close(STDERR_FILENO); |
568 | (void)close(pv[1]); | | 569 | (void)close(pv[1]); |
569 | | | 570 | |
570 | set[0].fd = s; | | 571 | set[0].fd = s; |
571 | set[0].events = POLLIN; | | 572 | set[0].events = POLLIN; |
572 | set[1].fd = pv[0]; | | 573 | set[1].fd = pv[0]; |
573 | set[1].events = POLLIN; | | 574 | set[1].events = POLLIN; |
574 | ioctl(pv[0], FIONBIO, (char *)&one); | | 575 | ioctl(pv[0], FIONBIO, (char *)&one); |
575 | | | 576 | |
576 | /* should set s nbio! */ | | 577 | /* should set s nbio! */ |
577 | do { | | 578 | do { |
578 | if (poll(set, 2, INFTIM) < 0) | | 579 | if (poll(set, 2, INFTIM) < 0) |
579 | break; | | 580 | break; |
580 | if (set[0].revents & POLLIN) { | | 581 | if (set[0].revents & POLLIN) { |
581 | int ret; | | 582 | int ret; |
582 | | | 583 | |
583 | ret = read(s, &sig, 1); | | 584 | ret = read(s, &sig, 1); |
584 | if (ret <= 0) | | 585 | if (ret <= 0) |
585 | set[0].events = 0; | | 586 | set[0].events = 0; |
586 | else | | 587 | else |
587 | killpg(pid, sig); | | 588 | killpg(pid, sig); |
588 | } | | 589 | } |
589 | if (set[1].revents & POLLIN) { | | 590 | if (set[1].revents & POLLIN) { |
590 | errno = 0; | | 591 | errno = 0; |
591 | cc = read(pv[0], buf, sizeof(buf)); | | 592 | cc = read(pv[0], buf, sizeof(buf)); |
592 | if (cc <= 0) { | | 593 | if (cc <= 0) { |
593 | shutdown(s, SHUT_RDWR); | | 594 | shutdown(s, SHUT_RDWR); |
594 | set[1].events = 0; | | 595 | set[1].events = 0; |
595 | } else { | | 596 | } else { |
596 | (void)write(s, buf, cc); | | 597 | (void)write(s, buf, cc); |
597 | } | | 598 | } |
598 | } | | 599 | } |
599 | | | 600 | |
600 | } while ((set[0].revents | set[1].revents) & POLLIN); | | 601 | } while ((set[0].revents | set[1].revents) & POLLIN); |
601 | PAM_END; | | 602 | PAM_END; |
602 | exit(EXIT_SUCCESS); | | 603 | exit(EXIT_SUCCESS); |
603 | } | | 604 | } |
604 | (void)close(s); | | 605 | (void)close(s); |
605 | (void)close(pv[0]); | | 606 | (void)close(pv[0]); |
606 | (void)dup2(pv[1], STDERR_FILENO); | | 607 | (void)dup2(pv[1], STDERR_FILENO); |
607 | close(pv[1]); | | 608 | close(pv[1]); |
608 | } | | 609 | } |
609 | #ifdef USE_PAM | | 610 | #ifdef USE_PAM |
610 | else { | | 611 | else { |
611 | pid = fork(); | | 612 | pid = fork(); |
612 | if (pid == -1) | | 613 | if (pid == -1) |
613 | rshd_errx(EXIT_FAILURE, "Can't fork. (%s)", | | 614 | rshd_errx(EXIT_FAILURE, "Can't fork. (%s)", |
614 | strerror(errno)); | | 615 | strerror(errno)); |
615 | if (pid) { | | 616 | if (pid) { |
616 | pid_t xpid; | | 617 | pid_t xpid; |
617 | int status; | | 618 | int status; |
618 | if ((xpid = waitpid(pid, &status, 0)) != pid) { | | 619 | if ((xpid = waitpid(pid, &status, 0)) != pid) { |
619 | pam_err = pam_close_session(pamh, 0); | | 620 | pam_err = pam_close_session(pamh, 0); |
620 | if (pam_err != PAM_SUCCESS) { | | 621 | if (pam_err != PAM_SUCCESS) { |
621 | syslog(LOG_ERR, | | 622 | syslog(LOG_ERR, |
622 | "pam_close_session: %s", | | 623 | "pam_close_session: %s", |
623 | pam_strerror(pamh, pam_err)); | | 624 | pam_strerror(pamh, pam_err)); |
624 | } | | 625 | } |
625 | PAM_END; | | 626 | PAM_END; |
626 | if (xpid != -1) | | 627 | if (xpid != -1) |
627 | syslog(LOG_WARNING, | | 628 | syslog(LOG_WARNING, |
628 | "wrong PID: %d != %d", pid, xpid); | | 629 | "wrong PID: %d != %d", pid, xpid); |
629 | else | | 630 | else |
630 | syslog(LOG_WARNING, | | 631 | syslog(LOG_WARNING, |
631 | "wait pid=%d failed %m", pid); | | 632 | "wait pid=%d failed %m", pid); |
632 | exit(EXIT_FAILURE); | | 633 | exit(EXIT_FAILURE); |
633 | } | | 634 | } |
634 | exit(EXIT_SUCCESS); | | 635 | exit(EXIT_SUCCESS); |
635 | } | | 636 | } |
636 | } | | 637 | } |
637 | #endif | | 638 | #endif |
638 | | | 639 | |
639 | #ifdef F_CLOSEM | | 640 | #ifdef F_CLOSEM |
640 | (void)fcntl(STDERR_FILENO + 1, F_CLOSEM, 0); | | 641 | (void)fcntl(STDERR_FILENO + 1, F_CLOSEM, 0); |
641 | #else | | 642 | #else |
642 | for (fd = getdtablesize(); fd > STDERR_FILENO; fd--) | | 643 | for (fd = getdtablesize(); fd > STDERR_FILENO; fd--) |
643 | (void)close(fd); | | 644 | (void)close(fd); |
644 | #endif | | 645 | #endif |
645 | if (setsid() == -1) | | 646 | if (setsid() == -1) |
646 | syslog(LOG_ERR, "setsid() failed: %m"); | | 647 | syslog(LOG_ERR, "setsid() failed: %m"); |
647 | #ifdef USE_PAM | | 648 | #ifdef USE_PAM |
648 | if (setlogin(pwd->pw_name) < 0) | | 649 | if (setlogin(pwd->pw_name) < 0) |
649 | syslog(LOG_ERR, "setlogin() failed: %m"); | | 650 | syslog(LOG_ERR, "setlogin() failed: %m"); |
650 | | | 651 | |
651 | if (*pwd->pw_shell == '\0') | | 652 | if (*pwd->pw_shell == '\0') |
652 | pwd->pw_shell = __UNCONST(_PATH_BSHELL); | | 653 | pwd->pw_shell = __UNCONST(_PATH_BSHELL); |
653 | | | 654 | |
654 | (void)pam_setenv(pamh, "HOME", pwd->pw_dir, 1); | | 655 | (void)pam_setenv(pamh, "HOME", pwd->pw_dir, 1); |
655 | (void)pam_setenv(pamh, "SHELL", pwd->pw_shell, 1); | | 656 | (void)pam_setenv(pamh, "SHELL", pwd->pw_shell, 1); |
656 | (void)pam_setenv(pamh, "USER", pwd->pw_name, 1); | | 657 | (void)pam_setenv(pamh, "USER", pwd->pw_name, 1); |
657 | (void)pam_setenv(pamh, "PATH", _PATH_DEFPATH, 1); | | 658 | (void)pam_setenv(pamh, "PATH", _PATH_DEFPATH, 1); |
658 | environ = pam_getenvlist(pamh); | | 659 | environ = pam_getenvlist(pamh); |
659 | (void)pam_end(pamh, pam_err); | | 660 | (void)pam_end(pamh, pam_err); |
660 | #else | | 661 | #else |
661 | #ifdef LOGIN_CAP | | 662 | #ifdef LOGIN_CAP |
662 | { | | 663 | { |
663 | char *sh; | | 664 | char *sh; |
664 | if ((sh = login_getcapstr(lc, "shell", NULL, NULL))) { | | 665 | if ((sh = login_getcapstr(lc, "shell", NULL, NULL))) { |
665 | if(!(sh = strdup(sh))) { | | 666 | if(!(sh = strdup(sh))) { |
666 | syslog(LOG_ERR, "Cannot alloc mem"); | | 667 | syslog(LOG_ERR, "Cannot alloc mem"); |
667 | exit(EXIT_FAILURE); | | 668 | exit(EXIT_FAILURE); |
668 | } | | 669 | } |
669 | pwd->pw_shell = sh; | | 670 | pwd->pw_shell = sh; |
670 | } | | 671 | } |
671 | } | | 672 | } |
672 | #endif | | 673 | #endif |
673 | environ = envinit; | | 674 | environ = envinit; |
674 | (void)strlcat(homedir, pwd->pw_dir, sizeof(homedir)); | | 675 | (void)strlcat(homedir, pwd->pw_dir, sizeof(homedir)); |
675 | (void)strlcat(path, _PATH_DEFPATH, sizeof(path)); | | 676 | (void)strlcat(path, _PATH_DEFPATH, sizeof(path)); |
676 | (void)strlcat(shell, pwd->pw_shell, sizeof(shell)); | | 677 | (void)strlcat(shell, pwd->pw_shell, sizeof(shell)); |
677 | (void)strlcat(username, pwd->pw_name, sizeof(username)); | | 678 | (void)strlcat(username, pwd->pw_name, sizeof(username)); |
678 | #endif | | 679 | #endif |
679 | | | 680 | |
680 | cp = strrchr(pwd->pw_shell, '/'); | | 681 | cp = strrchr(pwd->pw_shell, '/'); |
681 | if (cp) | | 682 | if (cp) |
682 | cp++; | | 683 | cp++; |
683 | else | | 684 | else |
684 | cp = pwd->pw_shell; | | 685 | cp = pwd->pw_shell; |
685 | | | 686 | |
686 | #ifdef LOGIN_CAP | | 687 | #ifdef LOGIN_CAP |
687 | if (setusercontext(lc, pwd, pwd->pw_uid, | | 688 | if (setusercontext(lc, pwd, pwd->pw_uid, |
688 | LOGIN_SETALL & ~LOGIN_SETGROUP) < 0) { | | 689 | LOGIN_SETALL & ~LOGIN_SETGROUP) < 0) { |
689 | syslog(LOG_ERR, "setusercontext(): %m"); | | 690 | syslog(LOG_ERR, "setusercontext(): %m"); |
690 | exit(EXIT_FAILURE); | | 691 | exit(EXIT_FAILURE); |
691 | } | | 692 | } |
692 | login_close(lc); | | 693 | login_close(lc); |
693 | #else | | 694 | #else |
694 | (void)setgid((gid_t)pwd->pw_gid); | | 695 | (void)setgid((gid_t)pwd->pw_gid); |
695 | (void)setuid((uid_t)pwd->pw_uid); | | 696 | (void)setuid((uid_t)pwd->pw_uid); |
696 | #endif | | 697 | #endif |
697 | endpwent(); | | 698 | endpwent(); |
698 | if (log_success || pwd->pw_uid == 0) { | | 699 | if (log_success || pwd->pw_uid == 0) { |
699 | syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: cmd='%.80s'", | | 700 | syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: cmd='%.80s'", |
700 | remuser, hostname, locuser, cmdbuf); | | 701 | remuser, hostname, locuser, cmdbuf); |
701 | } | | 702 | } |
702 | (void)execl(pwd->pw_shell, cp, "-c", cmdbuf, NULL); | | 703 | (void)execl(pwd->pw_shell, cp, "-c", cmdbuf, NULL); |
703 | rshd_errx(EXIT_FAILURE, "%s: %s", pwd->pw_shell, strerror(errno)); | | 704 | rshd_errx(EXIT_FAILURE, "%s: %s", pwd->pw_shell, strerror(errno)); |
704 | badlogin: | | 705 | badlogin: |
705 | syslog(LOG_INFO|LOG_AUTH, | | 706 | syslog(LOG_INFO|LOG_AUTH, |
706 | "%s@%s as %s: permission denied (%s). cmd='%.80s'", | | 707 | "%s@%s as %s: permission denied (%s). cmd='%.80s'", |
707 | remuser, hostname, locuser, errormsg, cmdbuf); | | 708 | remuser, hostname, locuser, errormsg, cmdbuf); |
708 | rshd_errx(EXIT_FAILURE, errorstr, errorhost); | | 709 | rshd_errx(EXIT_FAILURE, errorstr, errorhost); |
709 | } | | 710 | } |
710 | | | 711 | |
711 | /* | | 712 | /* |
712 | * Report error to client. Note: can't be used until second socket has | | 713 | * Report error to client. Note: can't be used until second socket has |
713 | * connected to client, or older clients will hang waiting for that | | 714 | * connected to client, or older clients will hang waiting for that |
714 | * connection first. | | 715 | * connection first. |
715 | */ | | 716 | */ |
716 | | | 717 | |
717 | #include <stdarg.h> | | 718 | #include <stdarg.h> |
718 | | | 719 | |
719 | void | | 720 | void |
720 | rshd_errx(int error, const char *fmt, ...) | | 721 | rshd_errx(int error, const char *fmt, ...) |
721 | { | | 722 | { |
722 | va_list ap; | | 723 | va_list ap; |
723 | int len, rv; | | 724 | int len, rv; |
724 | char *bp, buf[BUFSIZ]; | | 725 | char *bp, buf[BUFSIZ]; |
725 | va_start(ap, fmt); | | 726 | va_start(ap, fmt); |
726 | bp = buf; | | 727 | bp = buf; |
727 | if (sent_null == 0) { | | 728 | if (sent_null == 0) { |
728 | *bp++ = 1; | | 729 | *bp++ = 1; |
729 | len = 1; | | 730 | len = 1; |
730 | } else | | 731 | } else |
731 | len = 0; | | 732 | len = 0; |
732 | rv = vsnprintf(bp, sizeof(buf) - 2, fmt, ap); | | 733 | rv = vsnprintf(bp, sizeof(buf) - 2, fmt, ap); |
733 | bp[rv++] = '\n'; | | 734 | bp[rv++] = '\n'; |
734 | (void)write(STDERR_FILENO, buf, len + rv); | | 735 | (void)write(STDERR_FILENO, buf, len + rv); |
735 | va_end(ap); | | 736 | va_end(ap); |
736 | exit(error); | | 737 | exit(error); |
737 | } | | 738 | } |
738 | | | 739 | |
739 | void | | 740 | void |
740 | getstr(char *buf, int cnt, const char *err) | | 741 | getstr(char *buf, int cnt, const char *err) |
741 | { | | 742 | { |
742 | char c; | | 743 | char c; |
743 | | | 744 | |
744 | do { | | 745 | do { |
745 | if (read(STDIN_FILENO, &c, 1) != 1) | | 746 | if (read(STDIN_FILENO, &c, 1) != 1) |
746 | exit(EXIT_FAILURE); | | 747 | exit(EXIT_FAILURE); |
747 | *buf++ = c; | | 748 | *buf++ = c; |
748 | if (--cnt == 0) | | 749 | if (--cnt == 0) |
749 | rshd_errx(EXIT_FAILURE, "%s too long", err); | | 750 | rshd_errx(EXIT_FAILURE, "%s too long", err); |
750 | } while (c != 0); | | 751 | } while (c != 0); |
751 | } | | 752 | } |
752 | | | 753 | |
753 | /* | | 754 | /* |
754 | * Check whether host h is in our local domain, | | 755 | * Check whether host h is in our local domain, |
755 | * defined as sharing the last two components of the domain part, | | 756 | * defined as sharing the last two components of the domain part, |
756 | * or the entire domain part if the local domain has only one component. | | 757 | * or the entire domain part if the local domain has only one component. |
757 | * If either name is unqualified (contains no '.'), | | 758 | * If either name is unqualified (contains no '.'), |
758 | * assume that the host is local, as it will be | | 759 | * assume that the host is local, as it will be |
759 | * interpreted as such. | | 760 | * interpreted as such. |
760 | */ | | 761 | */ |
761 | int | | 762 | int |
762 | local_domain(char *h) | | 763 | local_domain(char *h) |
763 | { | | 764 | { |
764 | char localhost[MAXHOSTNAMELEN + 1]; | | 765 | char localhost[MAXHOSTNAMELEN + 1]; |
765 | char *p1, *p2; | | 766 | char *p1, *p2; |
766 | | | 767 | |
767 | localhost[0] = 0; | | 768 | localhost[0] = 0; |
768 | (void)gethostname(localhost, sizeof(localhost)); | | 769 | (void)gethostname(localhost, sizeof(localhost)); |
769 | localhost[sizeof(localhost) - 1] = '\0'; | | 770 | localhost[sizeof(localhost) - 1] = '\0'; |
770 | p1 = topdomain(localhost); | | 771 | p1 = topdomain(localhost); |
771 | p2 = topdomain(h); | | 772 | p2 = topdomain(h); |
772 | if (p1 == NULL || p2 == NULL || !strcasecmp(p1, p2)) | | 773 | if (p1 == NULL || p2 == NULL || !strcasecmp(p1, p2)) |
773 | return (1); | | 774 | return (1); |
774 | return (0); | | 775 | return (0); |
775 | } | | 776 | } |
776 | | | 777 | |
777 | char * | | 778 | char * |
778 | topdomain(char *h) | | 779 | topdomain(char *h) |
779 | { | | 780 | { |
780 | char *p, *maybe = NULL; | | 781 | char *p, *maybe = NULL; |
781 | int dots = 0; | | 782 | int dots = 0; |
782 | | | 783 | |
783 | for (p = h + strlen(h); p >= h; p--) { | | 784 | for (p = h + strlen(h); p >= h; p--) { |
784 | if (*p == '.') { | | 785 | if (*p == '.') { |
785 | if (++dots == 2) | | 786 | if (++dots == 2) |
786 | return (p); | | 787 | return (p); |
787 | maybe = p; | | 788 | maybe = p; |
788 | } | | 789 | } |
789 | } | | 790 | } |
790 | return (maybe); | | 791 | return (maybe); |
791 | } | | 792 | } |
792 | | | 793 | |
793 | void | | 794 | void |
794 | usage(void) | | 795 | usage(void) |
795 | { | | 796 | { |
796 | | | 797 | |
797 | syslog(LOG_ERR, "Usage: %s [-%s]", getprogname(), OPTIONS); | | 798 | syslog(LOG_ERR, "Usage: %s [-%s]", getprogname(), OPTIONS); |
798 | exit(EXIT_FAILURE); | | 799 | exit(EXIT_FAILURE); |
799 | } | | 800 | } |