diff options
Diffstat (limited to 'sshconnect.c')
-rw-r--r-- | sshconnect.c | 207 |
1 files changed, 96 insertions, 111 deletions
diff --git a/sshconnect.c b/sshconnect.c index e19392acf..fb6af67df 100644 --- a/sshconnect.c +++ b/sshconnect.c | |||
@@ -8,7 +8,7 @@ | |||
8 | */ | 8 | */ |
9 | 9 | ||
10 | #include "includes.h" | 10 | #include "includes.h" |
11 | RCSID("$Id: sshconnect.c,v 1.20 2000/01/03 12:41:05 damien Exp $"); | 11 | RCSID("$Id: sshconnect.c,v 1.21 2000/01/14 04:45:52 damien Exp $"); |
12 | 12 | ||
13 | #ifdef HAVE_OPENSSL | 13 | #ifdef HAVE_OPENSSL |
14 | #include <openssl/bn.h> | 14 | #include <openssl/bn.h> |
@@ -35,6 +35,7 @@ RCSID("$Id: sshconnect.c,v 1.20 2000/01/03 12:41:05 damien Exp $"); | |||
35 | unsigned char session_id[16]; | 35 | unsigned char session_id[16]; |
36 | 36 | ||
37 | extern Options options; | 37 | extern Options options; |
38 | extern char *__progname; | ||
38 | 39 | ||
39 | /* | 40 | /* |
40 | * Connect to the given ssh server using a proxy command. | 41 | * Connect to the given ssh server using a proxy command. |
@@ -48,10 +49,10 @@ ssh_proxy_connect(const char *host, u_short port, uid_t original_real_uid, | |||
48 | char *command_string; | 49 | char *command_string; |
49 | int pin[2], pout[2]; | 50 | int pin[2], pout[2]; |
50 | int pid; | 51 | int pid; |
51 | char portstring[100]; | 52 | char strport[NI_MAXSERV]; |
52 | 53 | ||
53 | /* Convert the port number into a string. */ | 54 | /* Convert the port number into a string. */ |
54 | snprintf(portstring, sizeof portstring, "%hu", port); | 55 | snprintf(strport, sizeof strport, "%hu", port); |
55 | 56 | ||
56 | /* Build the final command string in the buffer by making the | 57 | /* Build the final command string in the buffer by making the |
57 | appropriate substitutions to the given proxy command. */ | 58 | appropriate substitutions to the given proxy command. */ |
@@ -68,7 +69,7 @@ ssh_proxy_connect(const char *host, u_short port, uid_t original_real_uid, | |||
68 | continue; | 69 | continue; |
69 | } | 70 | } |
70 | if (cp[0] == '%' && cp[1] == 'p') { | 71 | if (cp[0] == '%' && cp[1] == 'p') { |
71 | buffer_append(&command, portstring, strlen(portstring)); | 72 | buffer_append(&command, strport, strlen(strport)); |
72 | cp++; | 73 | cp++; |
73 | continue; | 74 | continue; |
74 | } | 75 | } |
@@ -140,7 +141,7 @@ ssh_proxy_connect(const char *host, u_short port, uid_t original_real_uid, | |||
140 | * Creates a (possibly privileged) socket for use as the ssh connection. | 141 | * Creates a (possibly privileged) socket for use as the ssh connection. |
141 | */ | 142 | */ |
142 | int | 143 | int |
143 | ssh_create_socket(uid_t original_real_uid, int privileged) | 144 | ssh_create_socket(uid_t original_real_uid, int privileged, int family) |
144 | { | 145 | { |
145 | int sock; | 146 | int sock; |
146 | 147 | ||
@@ -150,10 +151,9 @@ ssh_create_socket(uid_t original_real_uid, int privileged) | |||
150 | */ | 151 | */ |
151 | if (privileged) { | 152 | if (privileged) { |
152 | int p = IPPORT_RESERVED - 1; | 153 | int p = IPPORT_RESERVED - 1; |
153 | 154 | sock = rresvport_af(&p, family); | |
154 | sock = rresvport(&p); | ||
155 | if (sock < 0) | 155 | if (sock < 0) |
156 | fatal("rresvport: %.100s", strerror(errno)); | 156 | fatal("rresvport: af=%d %.100s", family, strerror(errno)); |
157 | debug("Allocated local port %d.", p); | 157 | debug("Allocated local port %d.", p); |
158 | } else { | 158 | } else { |
159 | /* | 159 | /* |
@@ -161,17 +161,18 @@ ssh_create_socket(uid_t original_real_uid, int privileged) | |||
161 | * the user's uid to create the socket. | 161 | * the user's uid to create the socket. |
162 | */ | 162 | */ |
163 | temporarily_use_uid(original_real_uid); | 163 | temporarily_use_uid(original_real_uid); |
164 | sock = socket(AF_INET, SOCK_STREAM, 0); | 164 | sock = socket(family, SOCK_STREAM, 0); |
165 | if (sock < 0) | 165 | if (sock < 0) |
166 | fatal("socket: %.100s", strerror(errno)); | 166 | error("socket: %.100s", strerror(errno)); |
167 | restore_uid(); | 167 | restore_uid(); |
168 | } | 168 | } |
169 | return sock; | 169 | return sock; |
170 | } | 170 | } |
171 | 171 | ||
172 | /* | 172 | /* |
173 | * Opens a TCP/IP connection to the remote server on the given host. If | 173 | * Opens a TCP/IP connection to the remote server on the given host. |
174 | * port is 0, the default port will be used. If anonymous is zero, | 174 | * The address of the remote host will be returned in hostaddr. |
175 | * If port is 0, the default port will be used. If anonymous is zero, | ||
175 | * a privileged port will be allocated to make the connection. | 176 | * a privileged port will be allocated to make the connection. |
176 | * This requires super-user privileges if anonymous is false. | 177 | * This requires super-user privileges if anonymous is false. |
177 | * Connection_attempts specifies the maximum number of tries (one per | 178 | * Connection_attempts specifies the maximum number of tries (one per |
@@ -180,15 +181,16 @@ ssh_create_socket(uid_t original_real_uid, int privileged) | |||
180 | * the daemon. | 181 | * the daemon. |
181 | */ | 182 | */ |
182 | int | 183 | int |
183 | ssh_connect(const char *host, struct sockaddr_in * hostaddr, | 184 | ssh_connect(const char *host, struct sockaddr_storage * hostaddr, |
184 | u_short port, int connection_attempts, | 185 | u_short port, int connection_attempts, |
185 | int anonymous, uid_t original_real_uid, | 186 | int anonymous, uid_t original_real_uid, |
186 | const char *proxy_command) | 187 | const char *proxy_command) |
187 | { | 188 | { |
188 | int sock = -1, attempt, i; | 189 | int sock = -1, attempt; |
189 | int on = 1; | ||
190 | struct servent *sp; | 190 | struct servent *sp; |
191 | struct hostent *hp; | 191 | struct addrinfo hints, *ai, *aitop; |
192 | char ntop[NI_MAXHOST], strport[NI_MAXSERV]; | ||
193 | int gaierr; | ||
192 | struct linger linger; | 194 | struct linger linger; |
193 | 195 | ||
194 | debug("ssh_connect: getuid %d geteuid %d anon %d", | 196 | debug("ssh_connect: getuid %d geteuid %d anon %d", |
@@ -208,8 +210,13 @@ ssh_connect(const char *host, struct sockaddr_in * hostaddr, | |||
208 | 210 | ||
209 | /* No proxy command. */ | 211 | /* No proxy command. */ |
210 | 212 | ||
211 | /* No host lookup made yet. */ | 213 | memset(&hints, 0, sizeof(hints)); |
212 | hp = NULL; | 214 | hints.ai_family = IPv4or6; |
215 | hints.ai_socktype = SOCK_STREAM; | ||
216 | snprintf(strport, sizeof strport, "%d", port); | ||
217 | if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) | ||
218 | fatal("%s: %.100s: %s", __progname, host, | ||
219 | gai_strerror(gaierr)); | ||
213 | 220 | ||
214 | /* | 221 | /* |
215 | * Try to connect several times. On some machines, the first time | 222 | * Try to connect several times. On some machines, the first time |
@@ -220,82 +227,40 @@ ssh_connect(const char *host, struct sockaddr_in * hostaddr, | |||
220 | if (attempt > 0) | 227 | if (attempt > 0) |
221 | debug("Trying again..."); | 228 | debug("Trying again..."); |
222 | 229 | ||
223 | /* Try to parse the host name as a numeric inet address. */ | 230 | /* Loop through addresses for this host, and try each one in |
224 | memset(hostaddr, 0, sizeof(hostaddr)); | 231 | sequence until the connection succeeds. */ |
225 | hostaddr->sin_family = AF_INET; | 232 | for (ai = aitop; ai; ai = ai->ai_next) { |
226 | hostaddr->sin_port = htons(port); | 233 | if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) |
227 | hostaddr->sin_addr.s_addr = inet_addr(host); | 234 | continue; |
228 | if ((hostaddr->sin_addr.s_addr & 0xffffffff) != 0xffffffff) { | 235 | if (getnameinfo(ai->ai_addr, ai->ai_addrlen, |
229 | /* Valid numeric IP address */ | 236 | ntop, sizeof(ntop), strport, sizeof(strport), |
230 | debug("Connecting to %.100s port %d.", | 237 | NI_NUMERICHOST|NI_NUMERICSERV) != 0) { |
231 | inet_ntoa(hostaddr->sin_addr), port); | 238 | error("ssh_connect: getnameinfo failed"); |
232 | 239 | continue; | |
233 | /* Create a socket. */ | 240 | } |
234 | sock = ssh_create_socket(original_real_uid, | 241 | debug("Connecting to %.200s [%.100s] port %s.", |
235 | !anonymous && geteuid() == 0 && | 242 | host, ntop, strport); |
236 | port < IPPORT_RESERVED); | 243 | |
237 | 244 | /* Create a socket for connecting. */ | |
238 | /* | 245 | sock = ssh_create_socket(original_real_uid, |
239 | * Connect to the host. We use the user's uid in the | 246 | !anonymous && geteuid() == 0 && port < IPPORT_RESERVED, |
240 | * hope that it will help with the problems of | 247 | ai->ai_family); |
241 | * tcp_wrappers showing the remote uid as root. | 248 | if (sock < 0) |
249 | continue; | ||
250 | |||
251 | /* Connect to the host. We use the user's uid in the | ||
252 | * hope that it will help with tcp_wrappers showing | ||
253 | * the remote uid as root. | ||
242 | */ | 254 | */ |
243 | temporarily_use_uid(original_real_uid); | 255 | temporarily_use_uid(original_real_uid); |
244 | if (connect(sock, (struct sockaddr *) hostaddr, sizeof(*hostaddr)) | 256 | if (connect(sock, ai->ai_addr, ai->ai_addrlen) >= 0) { |
245 | >= 0) { | 257 | /* Successful connection. */ |
246 | /* Successful connect. */ | 258 | memcpy(hostaddr, ai->ai_addr, sizeof(*(ai->ai_addr))); |
247 | restore_uid(); | 259 | restore_uid(); |
248 | break; | 260 | break; |
249 | } | 261 | } else { |
250 | debug("connect: %.100s", strerror(errno)); | ||
251 | restore_uid(); | ||
252 | |||
253 | /* Destroy the failed socket. */ | ||
254 | shutdown(sock, SHUT_RDWR); | ||
255 | close(sock); | ||
256 | } else { | ||
257 | /* Not a valid numeric inet address. */ | ||
258 | /* Map host name to an address. */ | ||
259 | if (!hp) | ||
260 | hp = gethostbyname(host); | ||
261 | if (!hp) | ||
262 | fatal("Bad host name: %.100s", host); | ||
263 | if (!hp->h_addr_list[0]) | ||
264 | fatal("Host does not have an IP address: %.100s", host); | ||
265 | |||
266 | /* Loop through addresses for this host, and try | ||
267 | each one in sequence until the connection | ||
268 | succeeds. */ | ||
269 | for (i = 0; hp->h_addr_list[i]; i++) { | ||
270 | /* Set the address to connect to. */ | ||
271 | hostaddr->sin_family = hp->h_addrtype; | ||
272 | memcpy(&hostaddr->sin_addr, hp->h_addr_list[i], | ||
273 | sizeof(hostaddr->sin_addr)); | ||
274 | |||
275 | debug("Connecting to %.200s [%.100s] port %d.", | ||
276 | host, inet_ntoa(hostaddr->sin_addr), port); | ||
277 | |||
278 | /* Create a socket for connecting. */ | ||
279 | sock = ssh_create_socket(original_real_uid, | ||
280 | !anonymous && geteuid() == 0 && | ||
281 | port < IPPORT_RESERVED); | ||
282 | |||
283 | /* | ||
284 | * Connect to the host. We use the user's | ||
285 | * uid in the hope that it will help with | ||
286 | * tcp_wrappers showing the remote uid as | ||
287 | * root. | ||
288 | */ | ||
289 | temporarily_use_uid(original_real_uid); | ||
290 | if (connect(sock, (struct sockaddr *) hostaddr, | ||
291 | sizeof(*hostaddr)) >= 0) { | ||
292 | /* Successful connection. */ | ||
293 | restore_uid(); | ||
294 | break; | ||
295 | } | ||
296 | debug("connect: %.100s", strerror(errno)); | 262 | debug("connect: %.100s", strerror(errno)); |
297 | restore_uid(); | 263 | restore_uid(); |
298 | |||
299 | /* | 264 | /* |
300 | * Close the failed socket; there appear to | 265 | * Close the failed socket; there appear to |
301 | * be some problems when reusing a socket for | 266 | * be some problems when reusing a socket for |
@@ -305,13 +270,16 @@ ssh_connect(const char *host, struct sockaddr_in * hostaddr, | |||
305 | shutdown(sock, SHUT_RDWR); | 270 | shutdown(sock, SHUT_RDWR); |
306 | close(sock); | 271 | close(sock); |
307 | } | 272 | } |
308 | if (hp->h_addr_list[i]) | ||
309 | break; /* Successful connection. */ | ||
310 | } | 273 | } |
274 | if (ai) | ||
275 | break; /* Successful connection. */ | ||
311 | 276 | ||
312 | /* Sleep a moment before retrying. */ | 277 | /* Sleep a moment before retrying. */ |
313 | sleep(1); | 278 | sleep(1); |
314 | } | 279 | } |
280 | |||
281 | freeaddrinfo(aitop); | ||
282 | |||
315 | /* Return failure if we didn't get a successful connection. */ | 283 | /* Return failure if we didn't get a successful connection. */ |
316 | if (attempt >= connection_attempts) | 284 | if (attempt >= connection_attempts) |
317 | return 0; | 285 | return 0; |
@@ -323,7 +291,6 @@ ssh_connect(const char *host, struct sockaddr_in * hostaddr, | |||
323 | * as it has been closed for whatever reason. | 291 | * as it has been closed for whatever reason. |
324 | */ | 292 | */ |
325 | /* setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)); */ | 293 | /* setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)); */ |
326 | setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void *) &on, sizeof(on)); | ||
327 | linger.l_onoff = 1; | 294 | linger.l_onoff = 1; |
328 | linger.l_linger = 5; | 295 | linger.l_linger = 5; |
329 | setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *) &linger, sizeof(linger)); | 296 | setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *) &linger, sizeof(linger)); |
@@ -1095,17 +1062,43 @@ read_yes_or_no(const char *prompt, int defval) | |||
1095 | */ | 1062 | */ |
1096 | 1063 | ||
1097 | void | 1064 | void |
1098 | check_host_key(char *host, | 1065 | check_host_key(char *host, struct sockaddr *hostaddr, RSA *host_key) |
1099 | struct sockaddr_in *hostaddr, | ||
1100 | RSA *host_key) | ||
1101 | { | 1066 | { |
1102 | RSA *file_key; | 1067 | RSA *file_key; |
1103 | char *ip = NULL; | 1068 | char *ip = NULL; |
1104 | char hostline[1000], *hostp; | 1069 | char hostline[1000], *hostp; |
1105 | HostStatus host_status; | 1070 | HostStatus host_status; |
1106 | HostStatus ip_status; | 1071 | HostStatus ip_status; |
1107 | int host_ip_differ = 0; | 1072 | int local = 0, host_ip_differ = 0; |
1108 | int local = (ntohl(hostaddr->sin_addr.s_addr) >> 24) == IN_LOOPBACKNET; | 1073 | int sa_len; |
1074 | char ntop[NI_MAXHOST]; | ||
1075 | |||
1076 | /* | ||
1077 | * Force accepting of the host key for loopback/localhost. The | ||
1078 | * problem is that if the home directory is NFS-mounted to multiple | ||
1079 | * machines, localhost will refer to a different machine in each of | ||
1080 | * them, and the user will get bogus HOST_CHANGED warnings. This | ||
1081 | * essentially disables host authentication for localhost; however, | ||
1082 | * this is probably not a real problem. | ||
1083 | */ | ||
1084 | switch (hostaddr->sa_family) { | ||
1085 | case AF_INET: | ||
1086 | local = (ntohl(((struct sockaddr_in *)hostaddr)->sin_addr.s_addr) >> 24) == IN_LOOPBACKNET; | ||
1087 | sa_len = sizeof(struct sockaddr_in); | ||
1088 | break; | ||
1089 | case AF_INET6: | ||
1090 | local = IN6_IS_ADDR_LOOPBACK(&(((struct sockaddr_in6 *)hostaddr)->sin6_addr)); | ||
1091 | sa_len = sizeof(struct sockaddr_in6); | ||
1092 | break; | ||
1093 | default: | ||
1094 | local = 0; | ||
1095 | sa_len = sizeof(struct sockaddr_storage); | ||
1096 | break; | ||
1097 | } | ||
1098 | if (local) { | ||
1099 | debug("Forcing accepting of host key for loopback/localhost."); | ||
1100 | return; | ||
1101 | } | ||
1109 | 1102 | ||
1110 | /* | 1103 | /* |
1111 | * Turn off check_host_ip for proxy connects, since | 1104 | * Turn off check_host_ip for proxy connects, since |
@@ -1114,8 +1107,12 @@ check_host_key(char *host, | |||
1114 | if (options.proxy_command != NULL && options.check_host_ip) | 1107 | if (options.proxy_command != NULL && options.check_host_ip) |
1115 | options.check_host_ip = 0; | 1108 | options.check_host_ip = 0; |
1116 | 1109 | ||
1117 | if (options.check_host_ip) | 1110 | if (options.check_host_ip) { |
1118 | ip = xstrdup(inet_ntoa(hostaddr->sin_addr)); | 1111 | if (getnameinfo(hostaddr, sa_len, ntop, sizeof(ntop), |
1112 | NULL, 0, NI_NUMERICHOST) != 0) | ||
1113 | fatal("check_host_key: getnameinfo failed"); | ||
1114 | ip = xstrdup(ntop); | ||
1115 | } | ||
1119 | 1116 | ||
1120 | /* | 1117 | /* |
1121 | * Store the host key from the known host file in here so that we can | 1118 | * Store the host key from the known host file in here so that we can |
@@ -1137,18 +1134,6 @@ check_host_key(char *host, | |||
1137 | host_key->e, host_key->n, | 1134 | host_key->e, host_key->n, |
1138 | file_key->e, file_key->n); | 1135 | file_key->e, file_key->n); |
1139 | /* | 1136 | /* |
1140 | * Force accepting of the host key for localhost and 127.0.0.1. The | ||
1141 | * problem is that if the home directory is NFS-mounted to multiple | ||
1142 | * machines, localhost will refer to a different machine in each of | ||
1143 | * them, and the user will get bogus HOST_CHANGED warnings. This | ||
1144 | * essentially disables host authentication for localhost; however, | ||
1145 | * this is probably not a real problem. | ||
1146 | */ | ||
1147 | if (local) { | ||
1148 | debug("Forcing accepting of host key for localhost."); | ||
1149 | host_status = HOST_OK; | ||
1150 | } | ||
1151 | /* | ||
1152 | * Also perform check for the ip address, skip the check if we are | 1137 | * Also perform check for the ip address, skip the check if we are |
1153 | * localhost or the hostname was an ip address to begin with | 1138 | * localhost or the hostname was an ip address to begin with |
1154 | */ | 1139 | */ |
@@ -1301,7 +1286,7 @@ void | |||
1301 | ssh_login(int host_key_valid, | 1286 | ssh_login(int host_key_valid, |
1302 | RSA *own_host_key, | 1287 | RSA *own_host_key, |
1303 | const char *orighost, | 1288 | const char *orighost, |
1304 | struct sockaddr_in *hostaddr, | 1289 | struct sockaddr *hostaddr, |
1305 | uid_t original_real_uid) | 1290 | uid_t original_real_uid) |
1306 | { | 1291 | { |
1307 | int i, type; | 1292 | int i, type; |