Mercurial > dropbear
comparison dbutil.c @ 1069:2fa71c3b2827 pam
merge pam branch up to date
author | Matt Johnston <matt@ucc.asn.au> |
---|---|
date | Mon, 16 Mar 2015 21:34:05 +0800 |
parents | 01eea88963f3 |
children | 36557295418e |
comparison
equal
deleted
inserted
replaced
1068:9a6395ddb1b6 | 1069:2fa71c3b2827 |
---|---|
148 va_end(param); | 148 va_end(param); |
149 } | 149 } |
150 | 150 |
151 | 151 |
152 #ifdef DEBUG_TRACE | 152 #ifdef DEBUG_TRACE |
153 | |
154 static double debug_start_time = -1; | |
155 | |
156 void debug_start_net() | |
157 { | |
158 if (getenv("DROPBEAR_DEBUG_NET_TIMESTAMP")) | |
159 { | |
160 /* Timestamps start from first network activity */ | |
161 struct timeval tv; | |
162 gettimeofday(&tv, NULL); | |
163 debug_start_time = tv.tv_sec + (tv.tv_usec / 1000000.0); | |
164 TRACE(("Resetting Dropbear TRACE timestamps")) | |
165 } | |
166 } | |
167 | |
168 static double time_since_start() | |
169 { | |
170 double nowf; | |
171 struct timeval tv; | |
172 gettimeofday(&tv, NULL); | |
173 nowf = tv.tv_sec + (tv.tv_usec / 1000000.0); | |
174 if (debug_start_time < 0) | |
175 { | |
176 debug_start_time = nowf; | |
177 return 0; | |
178 } | |
179 return nowf - debug_start_time; | |
180 } | |
181 | |
153 void dropbear_trace(const char* format, ...) { | 182 void dropbear_trace(const char* format, ...) { |
154 va_list param; | 183 va_list param; |
155 struct timeval tv; | |
156 | 184 |
157 if (!debug_trace) { | 185 if (!debug_trace) { |
158 return; | 186 return; |
159 } | 187 } |
160 | 188 |
161 gettimeofday(&tv, NULL); | |
162 | |
163 va_start(param, format); | 189 va_start(param, format); |
164 fprintf(stderr, "TRACE (%d) %d.%d: ", getpid(), (int)tv.tv_sec, (int)tv.tv_usec); | 190 fprintf(stderr, "TRACE (%d) %f: ", getpid(), time_since_start()); |
165 vfprintf(stderr, format, param); | 191 vfprintf(stderr, format, param); |
166 fprintf(stderr, "\n"); | 192 fprintf(stderr, "\n"); |
167 va_end(param); | 193 va_end(param); |
168 } | 194 } |
169 | 195 |
170 void dropbear_trace2(const char* format, ...) { | 196 void dropbear_trace2(const char* format, ...) { |
171 static int trace_env = -1; | 197 static int trace_env = -1; |
172 va_list param; | 198 va_list param; |
173 struct timeval tv; | |
174 | 199 |
175 if (trace_env == -1) { | 200 if (trace_env == -1) { |
176 trace_env = getenv("DROPBEAR_TRACE2") ? 1 : 0; | 201 trace_env = getenv("DROPBEAR_TRACE2") ? 1 : 0; |
177 } | 202 } |
178 | 203 |
179 if (!(debug_trace && trace_env)) { | 204 if (!(debug_trace && trace_env)) { |
180 return; | 205 return; |
181 } | 206 } |
182 | 207 |
183 gettimeofday(&tv, NULL); | |
184 | |
185 va_start(param, format); | 208 va_start(param, format); |
186 fprintf(stderr, "TRACE2 (%d) %d.%d: ", getpid(), (int)tv.tv_sec, (int)tv.tv_usec); | 209 fprintf(stderr, "TRACE2 (%d) %f: ", getpid(), time_since_start()); |
187 vfprintf(stderr, format, param); | 210 vfprintf(stderr, format, param); |
188 fprintf(stderr, "\n"); | 211 fprintf(stderr, "\n"); |
189 va_end(param); | 212 va_end(param); |
190 } | 213 } |
191 #endif /* DEBUG_TRACE */ | 214 #endif /* DEBUG_TRACE */ |
192 | |
193 void set_sock_nodelay(int sock) { | |
194 int val; | |
195 | |
196 /* disable nagle */ | |
197 val = 1; | |
198 setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void*)&val, sizeof(val)); | |
199 } | |
200 | |
201 void set_sock_priority(int sock, enum dropbear_prio prio) { | |
202 | |
203 int iptos_val = 0, so_prio_val = 0, rc; | |
204 | |
205 /* Don't log ENOTSOCK errors so that this can harmlessly be called | |
206 * on a client '-J' proxy pipe */ | |
207 | |
208 /* set the TOS bit for either ipv4 or ipv6 */ | |
209 #ifdef IPTOS_LOWDELAY | |
210 if (prio == DROPBEAR_PRIO_LOWDELAY) { | |
211 iptos_val = IPTOS_LOWDELAY; | |
212 } else if (prio == DROPBEAR_PRIO_BULK) { | |
213 iptos_val = IPTOS_THROUGHPUT; | |
214 } | |
215 #if defined(IPPROTO_IPV6) && defined(IPV6_TCLASS) | |
216 rc = setsockopt(sock, IPPROTO_IPV6, IPV6_TCLASS, (void*)&iptos_val, sizeof(iptos_val)); | |
217 if (rc < 0 && errno != ENOTSOCK) { | |
218 TRACE(("Couldn't set IPV6_TCLASS (%s)", strerror(errno))); | |
219 } | |
220 #endif | |
221 rc = setsockopt(sock, IPPROTO_IP, IP_TOS, (void*)&iptos_val, sizeof(iptos_val)); | |
222 if (rc < 0 && errno != ENOTSOCK) { | |
223 TRACE(("Couldn't set IP_TOS (%s)", strerror(errno))); | |
224 } | |
225 #endif | |
226 | |
227 #ifdef SO_PRIORITY | |
228 if (prio == DROPBEAR_PRIO_LOWDELAY) { | |
229 so_prio_val = TC_PRIO_INTERACTIVE; | |
230 } else if (prio == DROPBEAR_PRIO_BULK) { | |
231 so_prio_val = TC_PRIO_BULK; | |
232 } | |
233 /* linux specific, sets QoS class. see tc-prio(8) */ | |
234 rc = setsockopt(sock, SOL_SOCKET, SO_PRIORITY, (void*) &so_prio_val, sizeof(so_prio_val)); | |
235 if (rc < 0 && errno != ENOTSOCK) | |
236 dropbear_log(LOG_WARNING, "Couldn't set SO_PRIORITY (%s)", | |
237 strerror(errno)); | |
238 #endif | |
239 | |
240 } | |
241 | |
242 /* Listen on address:port. | |
243 * Special cases are address of "" listening on everything, | |
244 * and address of NULL listening on localhost only. | |
245 * Returns the number of sockets bound on success, or -1 on failure. On | |
246 * failure, if errstring wasn't NULL, it'll be a newly malloced error | |
247 * string.*/ | |
248 int dropbear_listen(const char* address, const char* port, | |
249 int *socks, unsigned int sockcount, char **errstring, int *maxfd) { | |
250 | |
251 struct addrinfo hints, *res = NULL, *res0 = NULL; | |
252 int err; | |
253 unsigned int nsock; | |
254 struct linger linger; | |
255 int val; | |
256 int sock; | |
257 | |
258 TRACE(("enter dropbear_listen")) | |
259 | |
260 memset(&hints, 0, sizeof(hints)); | |
261 hints.ai_family = AF_UNSPEC; /* TODO: let them flag v4 only etc */ | |
262 hints.ai_socktype = SOCK_STREAM; | |
263 | |
264 /* for calling getaddrinfo: | |
265 address == NULL and !AI_PASSIVE: local loopback | |
266 address == NULL and AI_PASSIVE: all interfaces | |
267 address != NULL: whatever the address says */ | |
268 if (!address) { | |
269 TRACE(("dropbear_listen: local loopback")) | |
270 } else { | |
271 if (address[0] == '\0') { | |
272 TRACE(("dropbear_listen: all interfaces")) | |
273 address = NULL; | |
274 } | |
275 hints.ai_flags = AI_PASSIVE; | |
276 } | |
277 err = getaddrinfo(address, port, &hints, &res0); | |
278 | |
279 if (err) { | |
280 if (errstring != NULL && *errstring == NULL) { | |
281 int len; | |
282 len = 20 + strlen(gai_strerror(err)); | |
283 *errstring = (char*)m_malloc(len); | |
284 snprintf(*errstring, len, "Error resolving: %s", gai_strerror(err)); | |
285 } | |
286 if (res0) { | |
287 freeaddrinfo(res0); | |
288 res0 = NULL; | |
289 } | |
290 TRACE(("leave dropbear_listen: failed resolving")) | |
291 return -1; | |
292 } | |
293 | |
294 | |
295 nsock = 0; | |
296 for (res = res0; res != NULL && nsock < sockcount; | |
297 res = res->ai_next) { | |
298 | |
299 /* Get a socket */ | |
300 socks[nsock] = socket(res->ai_family, res->ai_socktype, | |
301 res->ai_protocol); | |
302 | |
303 sock = socks[nsock]; /* For clarity */ | |
304 | |
305 if (sock < 0) { | |
306 err = errno; | |
307 TRACE(("socket() failed")) | |
308 continue; | |
309 } | |
310 | |
311 /* Various useful socket options */ | |
312 val = 1; | |
313 /* set to reuse, quick timeout */ | |
314 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void*) &val, sizeof(val)); | |
315 linger.l_onoff = 1; | |
316 linger.l_linger = 5; | |
317 setsockopt(sock, SOL_SOCKET, SO_LINGER, (void*)&linger, sizeof(linger)); | |
318 | |
319 #if defined(IPPROTO_IPV6) && defined(IPV6_V6ONLY) | |
320 if (res->ai_family == AF_INET6) { | |
321 int on = 1; | |
322 if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, | |
323 &on, sizeof(on)) == -1) { | |
324 dropbear_log(LOG_WARNING, "Couldn't set IPV6_V6ONLY"); | |
325 } | |
326 } | |
327 #endif | |
328 | |
329 set_sock_nodelay(sock); | |
330 | |
331 if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) { | |
332 err = errno; | |
333 close(sock); | |
334 TRACE(("bind(%s) failed", port)) | |
335 continue; | |
336 } | |
337 | |
338 if (listen(sock, DROPBEAR_LISTEN_BACKLOG) < 0) { | |
339 err = errno; | |
340 close(sock); | |
341 TRACE(("listen() failed")) | |
342 continue; | |
343 } | |
344 | |
345 *maxfd = MAX(*maxfd, sock); | |
346 | |
347 nsock++; | |
348 } | |
349 | |
350 if (res0) { | |
351 freeaddrinfo(res0); | |
352 res0 = NULL; | |
353 } | |
354 | |
355 if (nsock == 0) { | |
356 if (errstring != NULL && *errstring == NULL) { | |
357 int len; | |
358 len = 20 + strlen(strerror(err)); | |
359 *errstring = (char*)m_malloc(len); | |
360 snprintf(*errstring, len, "Error listening: %s", strerror(err)); | |
361 } | |
362 TRACE(("leave dropbear_listen: failure, %s", strerror(err))) | |
363 return -1; | |
364 } | |
365 | |
366 TRACE(("leave dropbear_listen: success, %d socks bound", nsock)) | |
367 return nsock; | |
368 } | |
369 | 215 |
370 /* Connect to a given unix socket. The socket is blocking */ | 216 /* Connect to a given unix socket. The socket is blocking */ |
371 #ifdef ENABLE_CONNECT_UNIX | 217 #ifdef ENABLE_CONNECT_UNIX |
372 int connect_unix(const char* path) { | 218 int connect_unix(const char* path) { |
373 struct sockaddr_un addr; | 219 struct sockaddr_un addr; |
387 return -1; | 233 return -1; |
388 } | 234 } |
389 return fd; | 235 return fd; |
390 } | 236 } |
391 #endif | 237 #endif |
392 | |
393 /* Connect via TCP to a host. Connection will try ipv4 or ipv6, will | |
394 * return immediately if nonblocking is set. On failure, if errstring | |
395 * wasn't null, it will be a newly malloced error message */ | |
396 | |
397 /* TODO: maxfd */ | |
398 int connect_remote(const char* remotehost, const char* remoteport, | |
399 int nonblocking, char ** errstring) { | |
400 | |
401 struct addrinfo *res0 = NULL, *res = NULL, hints; | |
402 int sock; | |
403 int err; | |
404 | |
405 TRACE(("enter connect_remote")) | |
406 | |
407 if (errstring != NULL) { | |
408 *errstring = NULL; | |
409 } | |
410 | |
411 memset(&hints, 0, sizeof(hints)); | |
412 hints.ai_socktype = SOCK_STREAM; | |
413 hints.ai_family = PF_UNSPEC; | |
414 | |
415 err = getaddrinfo(remotehost, remoteport, &hints, &res0); | |
416 if (err) { | |
417 if (errstring != NULL && *errstring == NULL) { | |
418 int len; | |
419 len = 100 + strlen(gai_strerror(err)); | |
420 *errstring = (char*)m_malloc(len); | |
421 snprintf(*errstring, len, "Error resolving '%s' port '%s'. %s", | |
422 remotehost, remoteport, gai_strerror(err)); | |
423 } | |
424 TRACE(("Error resolving: %s", gai_strerror(err))) | |
425 return -1; | |
426 } | |
427 | |
428 sock = -1; | |
429 err = EADDRNOTAVAIL; | |
430 for (res = res0; res; res = res->ai_next) { | |
431 | |
432 sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); | |
433 if (sock < 0) { | |
434 err = errno; | |
435 continue; | |
436 } | |
437 | |
438 if (nonblocking) { | |
439 setnonblocking(sock); | |
440 } | |
441 | |
442 if (connect(sock, res->ai_addr, res->ai_addrlen) < 0) { | |
443 if (errno == EINPROGRESS && nonblocking) { | |
444 TRACE(("Connect in progress")) | |
445 break; | |
446 } else { | |
447 err = errno; | |
448 close(sock); | |
449 sock = -1; | |
450 continue; | |
451 } | |
452 } | |
453 | |
454 break; /* Success */ | |
455 } | |
456 | |
457 if (sock < 0 && !(errno == EINPROGRESS && nonblocking)) { | |
458 /* Failed */ | |
459 if (errstring != NULL && *errstring == NULL) { | |
460 int len; | |
461 len = 20 + strlen(strerror(err)); | |
462 *errstring = (char*)m_malloc(len); | |
463 snprintf(*errstring, len, "Error connecting: %s", strerror(err)); | |
464 } | |
465 TRACE(("Error connecting: %s", strerror(err))) | |
466 } else { | |
467 /* Success */ | |
468 set_sock_nodelay(sock); | |
469 } | |
470 | |
471 freeaddrinfo(res0); | |
472 if (sock > 0 && errstring != NULL && *errstring != NULL) { | |
473 m_free(*errstring); | |
474 } | |
475 | |
476 TRACE(("leave connect_remote: sock %d\n", sock)) | |
477 return sock; | |
478 } | |
479 | 238 |
480 /* Sets up a pipe for a, returning three non-blocking file descriptors | 239 /* Sets up a pipe for a, returning three non-blocking file descriptors |
481 * and the pid. exec_fn is the function that will actually execute the child process, | 240 * and the pid. exec_fn is the function that will actually execute the child process, |
482 * it will be run after the child has fork()ed, and is passed exec_data. | 241 * it will be run after the child has fork()ed, and is passed exec_data. |
483 * If ret_errfd == NULL then stderr will not be captured. | 242 * If ret_errfd == NULL then stderr will not be captured. |
610 } | 369 } |
611 | 370 |
612 execv(usershell, argv); | 371 execv(usershell, argv); |
613 } | 372 } |
614 | 373 |
615 void get_socket_address(int fd, char **local_host, char **local_port, | |
616 char **remote_host, char **remote_port, int host_lookup) | |
617 { | |
618 struct sockaddr_storage addr; | |
619 socklen_t addrlen; | |
620 | |
621 if (local_host || local_port) { | |
622 addrlen = sizeof(addr); | |
623 if (getsockname(fd, (struct sockaddr*)&addr, &addrlen) < 0) { | |
624 dropbear_exit("Failed socket address: %s", strerror(errno)); | |
625 } | |
626 getaddrstring(&addr, local_host, local_port, host_lookup); | |
627 } | |
628 if (remote_host || remote_port) { | |
629 addrlen = sizeof(addr); | |
630 if (getpeername(fd, (struct sockaddr*)&addr, &addrlen) < 0) { | |
631 dropbear_exit("Failed socket address: %s", strerror(errno)); | |
632 } | |
633 getaddrstring(&addr, remote_host, remote_port, host_lookup); | |
634 } | |
635 } | |
636 | |
637 /* Return a string representation of the socket address passed. The return | |
638 * value is allocated with malloc() */ | |
639 void getaddrstring(struct sockaddr_storage* addr, | |
640 char **ret_host, char **ret_port, | |
641 int host_lookup) { | |
642 | |
643 char host[NI_MAXHOST+1], serv[NI_MAXSERV+1]; | |
644 unsigned int len; | |
645 int ret; | |
646 | |
647 int flags = NI_NUMERICSERV | NI_NUMERICHOST; | |
648 | |
649 #ifndef DO_HOST_LOOKUP | |
650 host_lookup = 0; | |
651 #endif | |
652 | |
653 if (host_lookup) { | |
654 flags = NI_NUMERICSERV; | |
655 } | |
656 | |
657 len = sizeof(struct sockaddr_storage); | |
658 /* Some platforms such as Solaris 8 require that len is the length | |
659 * of the specific structure. Some older linux systems (glibc 2.1.3 | |
660 * such as debian potato) have sockaddr_storage.__ss_family instead | |
661 * but we'll ignore them */ | |
662 #ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY | |
663 if (addr->ss_family == AF_INET) { | |
664 len = sizeof(struct sockaddr_in); | |
665 } | |
666 #ifdef AF_INET6 | |
667 if (addr->ss_family == AF_INET6) { | |
668 len = sizeof(struct sockaddr_in6); | |
669 } | |
670 #endif | |
671 #endif | |
672 | |
673 ret = getnameinfo((struct sockaddr*)addr, len, host, sizeof(host)-1, | |
674 serv, sizeof(serv)-1, flags); | |
675 | |
676 if (ret != 0) { | |
677 if (host_lookup) { | |
678 /* On some systems (Darwin does it) we get EINTR from getnameinfo | |
679 * somehow. Eew. So we'll just return the IP, since that doesn't seem | |
680 * to exhibit that behaviour. */ | |
681 getaddrstring(addr, ret_host, ret_port, 0); | |
682 return; | |
683 } else { | |
684 /* if we can't do a numeric lookup, something's gone terribly wrong */ | |
685 dropbear_exit("Failed lookup: %s", gai_strerror(ret)); | |
686 } | |
687 } | |
688 | |
689 if (ret_host) { | |
690 *ret_host = m_strdup(host); | |
691 } | |
692 if (ret_port) { | |
693 *ret_port = m_strdup(serv); | |
694 } | |
695 } | |
696 | |
697 #ifdef DEBUG_TRACE | 374 #ifdef DEBUG_TRACE |
698 void printhex(const char * label, const unsigned char * buf, int len) { | 375 void printhex(const char * label, const unsigned char * buf, int len) { |
699 | 376 |
700 int i; | 377 int i; |
701 | 378 |
825 } | 502 } |
826 #endif | 503 #endif |
827 | 504 |
828 /* make sure that the socket closes */ | 505 /* make sure that the socket closes */ |
829 void m_close(int fd) { | 506 void m_close(int fd) { |
507 int val; | |
830 | 508 |
831 if (fd == -1) { | 509 if (fd == -1) { |
832 return; | 510 return; |
833 } | 511 } |
834 | 512 |
835 int val; | |
836 do { | 513 do { |
837 val = close(fd); | 514 val = close(fd); |
838 } while (val < 0 && errno == EINTR); | 515 } while (val < 0 && errno == EINTR); |
839 | 516 |
840 if (val < 0 && errno != EBADF) { | 517 if (val < 0 && errno != EBADF) { |
934 *val = l; | 611 *val = l; |
935 return DROPBEAR_SUCCESS; | 612 return DROPBEAR_SUCCESS; |
936 } | 613 } |
937 } | 614 } |
938 | 615 |
616 /* Returns malloced path. Only expands ~ in first character */ | |
617 char * expand_tilde(const char *inpath) { | |
618 struct passwd *pw = NULL; | |
619 if (inpath[0] == '~') { | |
620 pw = getpwuid(getuid()); | |
621 if (pw && pw->pw_dir) { | |
622 int len = strlen(inpath) + strlen(pw->pw_dir) + 1; | |
623 char *buf = m_malloc(len); | |
624 snprintf(buf, len, "%s/%s", pw->pw_dir, &inpath[1]); | |
625 return buf; | |
626 } | |
627 } | |
628 | |
629 /* Fallback */ | |
630 return m_strdup(inpath); | |
631 } | |
632 | |
939 int constant_time_memcmp(const void* a, const void *b, size_t n) | 633 int constant_time_memcmp(const void* a, const void *b, size_t n) |
940 { | 634 { |
941 const char *xa = a, *xb = b; | 635 const char *xa = a, *xb = b; |
942 uint8_t c = 0; | 636 uint8_t c = 0; |
943 size_t i; | 637 size_t i; |
999 | 693 |
1000 /* Fallback for everything else - this will sometimes go backwards */ | 694 /* Fallback for everything else - this will sometimes go backwards */ |
1001 return time(NULL); | 695 return time(NULL); |
1002 } | 696 } |
1003 | 697 |
698 |