summaryrefslogtreecommitdiff
path: root/sshconnect.c
diff options
context:
space:
mode:
Diffstat (limited to 'sshconnect.c')
-rw-r--r--sshconnect.c207
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"
11RCSID("$Id: sshconnect.c,v 1.20 2000/01/03 12:41:05 damien Exp $"); 11RCSID("$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 $");
35unsigned char session_id[16]; 35unsigned char session_id[16];
36 36
37extern Options options; 37extern Options options;
38extern 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 */
142int 143int
143ssh_create_socket(uid_t original_real_uid, int privileged) 144ssh_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 */
182int 183int
183ssh_connect(const char *host, struct sockaddr_in * hostaddr, 184ssh_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
1097void 1064void
1098check_host_key(char *host, 1065check_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
1301ssh_login(int host_key_valid, 1286ssh_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;