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