summaryrefslogtreecommitdiff
path: root/misc.c
diff options
context:
space:
mode:
authormillert@openbsd.org <millert@openbsd.org>2017-10-21 23:06:24 +0000
committerDamien Miller <djm@mindrot.org>2017-10-23 16:10:08 +1100
commit887669ef032d63cf07f53cada216fa8a0c9a7d72 (patch)
tree089b20255da21a489d7bc796a8ee86bd0b8f028f /misc.c
parentd27bff293cfeb2252f4c7a58babe5ad3262c6c98 (diff)
upstream commit
Add URI support to ssh, sftp and scp. For example ssh://user@host or sftp://user@host/path. The connection parameters described in draft-ietf-secsh-scp-sftp-ssh-uri-04 are not implemented since the ssh fingerprint format in the draft uses md5 with no way to specify the hash function type. OK djm@ Upstream-ID: 4ba3768b662d6722de59e6ecb00abf2d4bf9cacc
Diffstat (limited to 'misc.c')
-rw-r--r--misc.c297
1 files changed, 292 insertions, 5 deletions
diff --git a/misc.c b/misc.c
index 05950a471..d4d0e44a5 100644
--- a/misc.c
+++ b/misc.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: misc.c,v 1.113 2017/08/18 05:48:04 djm Exp $ */ 1/* $OpenBSD: misc.c,v 1.114 2017/10/21 23:06:24 millert Exp $ */
2/* 2/*
3 * Copyright (c) 2000 Markus Friedl. All rights reserved. 3 * Copyright (c) 2000 Markus Friedl. All rights reserved.
4 * Copyright (c) 2005,2006 Damien Miller. All rights reserved. 4 * Copyright (c) 2005,2006 Damien Miller. All rights reserved.
@@ -395,11 +395,12 @@ put_host_port(const char *host, u_short port)
395 * Search for next delimiter between hostnames/addresses and ports. 395 * Search for next delimiter between hostnames/addresses and ports.
396 * Argument may be modified (for termination). 396 * Argument may be modified (for termination).
397 * Returns *cp if parsing succeeds. 397 * Returns *cp if parsing succeeds.
398 * *cp is set to the start of the next delimiter, if one was found. 398 * *cp is set to the start of the next field, if one was found.
399 * The delimiter char, if present, is stored in delim.
399 * If this is the last field, *cp is set to NULL. 400 * If this is the last field, *cp is set to NULL.
400 */ 401 */
401char * 402static char *
402hpdelim(char **cp) 403hpdelim2(char **cp, char *delim)
403{ 404{
404 char *s, *old; 405 char *s, *old;
405 406
@@ -422,6 +423,8 @@ hpdelim(char **cp)
422 423
423 case ':': 424 case ':':
424 case '/': 425 case '/':
426 if (delim != NULL)
427 *delim = *s;
425 *s = '\0'; /* terminate */ 428 *s = '\0'; /* terminate */
426 *cp = s + 1; 429 *cp = s + 1;
427 break; 430 break;
@@ -434,6 +437,12 @@ hpdelim(char **cp)
434} 437}
435 438
436char * 439char *
440hpdelim(char **cp)
441{
442 return hpdelim2(cp, NULL);
443}
444
445char *
437cleanhostname(char *host) 446cleanhostname(char *host)
438{ 447{
439 if (*host == '[' && host[strlen(host) - 1] == ']') { 448 if (*host == '[' && host[strlen(host) - 1] == ']') {
@@ -467,6 +476,75 @@ colon(char *cp)
467} 476}
468 477
469/* 478/*
479 * Parse a [user@]host:[path] string.
480 * Caller must free returned user, host and path.
481 * Any of the pointer return arguments may be NULL (useful for syntax checking).
482 * If user was not specified then *userp will be set to NULL.
483 * If host was not specified then *hostp will be set to NULL.
484 * If path was not specified then *pathp will be set to ".".
485 * Returns 0 on success, -1 on failure.
486 */
487int
488parse_user_host_path(const char *s, char **userp, char **hostp, char **pathp)
489{
490 char *user = NULL, *host = NULL, *path = NULL;
491 char *sdup, *tmp;
492 int ret = -1;
493
494 if (userp != NULL)
495 *userp = NULL;
496 if (hostp != NULL)
497 *hostp = NULL;
498 if (pathp != NULL)
499 *pathp = NULL;
500
501 sdup = tmp = xstrdup(s);
502
503 /* Check for remote syntax: [user@]host:[path] */
504 if ((tmp = colon(sdup)) == NULL)
505 goto out;
506
507 /* Extract optional path */
508 *tmp++ = '\0';
509 if (*tmp == '\0')
510 tmp = ".";
511 path = xstrdup(tmp);
512
513 /* Extract optional user and mandatory host */
514 tmp = strrchr(sdup, '@');
515 if (tmp != NULL) {
516 *tmp++ = '\0';
517 host = xstrdup(cleanhostname(tmp));
518 if (*sdup != '\0')
519 user = xstrdup(sdup);
520 } else {
521 host = xstrdup(cleanhostname(sdup));
522 user = NULL;
523 }
524
525 /* Success */
526 if (userp != NULL) {
527 *userp = user;
528 user = NULL;
529 }
530 if (hostp != NULL) {
531 *hostp = host;
532 host = NULL;
533 }
534 if (pathp != NULL) {
535 *pathp = path;
536 path = NULL;
537 }
538 ret = 0;
539out:
540 free(sdup);
541 free(user);
542 free(host);
543 free(path);
544 return ret;
545}
546
547/*
470 * Parse a [user@]host[:port] string. 548 * Parse a [user@]host[:port] string.
471 * Caller must free returned user and host. 549 * Caller must free returned user and host.
472 * Any of the pointer return arguments may be NULL (useful for syntax checking). 550 * Any of the pointer return arguments may be NULL (useful for syntax checking).
@@ -491,7 +569,7 @@ parse_user_host_port(const char *s, char **userp, char **hostp, int *portp)
491 if ((sdup = tmp = strdup(s)) == NULL) 569 if ((sdup = tmp = strdup(s)) == NULL)
492 return -1; 570 return -1;
493 /* Extract optional username */ 571 /* Extract optional username */
494 if ((cp = strchr(tmp, '@')) != NULL) { 572 if ((cp = strrchr(tmp, '@')) != NULL) {
495 *cp = '\0'; 573 *cp = '\0';
496 if (*tmp == '\0') 574 if (*tmp == '\0')
497 goto out; 575 goto out;
@@ -527,6 +605,168 @@ parse_user_host_port(const char *s, char **userp, char **hostp, int *portp)
527 return ret; 605 return ret;
528} 606}
529 607
608/*
609 * Converts a two-byte hex string to decimal.
610 * Returns the decimal value or -1 for invalid input.
611 */
612static int
613hexchar(const char *s)
614{
615 unsigned char result[2];
616 int i;
617
618 for (i = 0; i < 2; i++) {
619 if (s[i] >= '0' && s[i] <= '9')
620 result[i] = (unsigned char)(s[i] - '0');
621 else if (s[i] >= 'a' && s[i] <= 'f')
622 result[i] = (unsigned char)(s[i] - 'a') + 10;
623 else if (s[i] >= 'A' && s[i] <= 'F')
624 result[i] = (unsigned char)(s[i] - 'A') + 10;
625 else
626 return -1;
627 }
628 return (result[0] << 4) | result[1];
629}
630
631/*
632 * Decode an url-encoded string.
633 * Returns a newly allocated string on success or NULL on failure.
634 */
635static char *
636urldecode(const char *src)
637{
638 char *ret, *dst;
639 int ch;
640
641 ret = xmalloc(strlen(src) + 1);
642 for (dst = ret; *src != '\0'; src++) {
643 switch (*src) {
644 case '+':
645 *dst++ = ' ';
646 break;
647 case '%':
648 if (!isxdigit((unsigned char)src[1]) ||
649 !isxdigit((unsigned char)src[2]) ||
650 (ch = hexchar(src + 1)) == -1) {
651 free(ret);
652 return NULL;
653 }
654 *dst++ = ch;
655 src += 2;
656 break;
657 default:
658 *dst++ = *src;
659 break;
660 }
661 }
662 *dst = '\0';
663
664 return ret;
665}
666
667/*
668 * Parse an (scp|ssh|sftp)://[user@]host[:port][/path] URI.
669 * See https://tools.ietf.org/html/draft-ietf-secsh-scp-sftp-ssh-uri-04
670 * Either user or path may be url-encoded (but not host or port).
671 * Caller must free returned user, host and path.
672 * Any of the pointer return arguments may be NULL (useful for syntax checking)
673 * but the scheme must always be specified.
674 * If user was not specified then *userp will be set to NULL.
675 * If port was not specified then *portp will be -1.
676 * If path was not specified then *pathp will be set to NULL.
677 * Returns 0 on success, 1 if non-uri/wrong scheme, -1 on error/invalid uri.
678 */
679int
680parse_uri(const char *scheme, const char *uri, char **userp, char **hostp,
681 int *portp, char **pathp)
682{
683 char *uridup, *cp, *tmp, ch;
684 char *user = NULL, *host = NULL, *path = NULL;
685 int port = -1, ret = -1;
686 size_t len;
687
688 len = strlen(scheme);
689 if (strncmp(uri, scheme, len) != 0 || strncmp(uri + len, "://", 3) != 0)
690 return 1;
691 uri += len + 3;
692
693 if (userp != NULL)
694 *userp = NULL;
695 if (hostp != NULL)
696 *hostp = NULL;
697 if (portp != NULL)
698 *portp = -1;
699 if (pathp != NULL)
700 *pathp = NULL;
701
702 uridup = tmp = xstrdup(uri);
703
704 /* Extract optional ssh-info (username + connection params) */
705 if ((cp = strchr(tmp, '@')) != NULL) {
706 char *delim;
707
708 *cp = '\0';
709 /* Extract username and connection params */
710 if ((delim = strchr(tmp, ';')) != NULL) {
711 /* Just ignore connection params for now */
712 *delim = '\0';
713 }
714 if (*tmp == '\0') {
715 /* Empty username */
716 goto out;
717 }
718 if ((user = urldecode(tmp)) == NULL)
719 goto out;
720 tmp = cp + 1;
721 }
722
723 /* Extract mandatory hostname */
724 if ((cp = hpdelim2(&tmp, &ch)) == NULL || *cp == '\0')
725 goto out;
726 host = xstrdup(cleanhostname(cp));
727 if (!valid_domain(host, 0, NULL))
728 goto out;
729
730 if (tmp != NULL && *tmp != '\0') {
731 if (ch == ':') {
732 /* Convert and verify port. */
733 if ((cp = strchr(tmp, '/')) != NULL)
734 *cp = '\0';
735 if ((port = a2port(tmp)) <= 0)
736 goto out;
737 tmp = cp ? cp + 1 : NULL;
738 }
739 if (tmp != NULL && *tmp != '\0') {
740 /* Extract optional path */
741 if ((path = urldecode(tmp)) == NULL)
742 goto out;
743 }
744 }
745
746 /* Success */
747 if (userp != NULL) {
748 *userp = user;
749 user = NULL;
750 }
751 if (hostp != NULL) {
752 *hostp = host;
753 host = NULL;
754 }
755 if (portp != NULL)
756 *portp = port;
757 if (pathp != NULL) {
758 *pathp = path;
759 path = NULL;
760 }
761 ret = 0;
762 out:
763 free(uridup);
764 free(user);
765 free(host);
766 free(path);
767 return ret;
768}
769
530/* function to assist building execv() arguments */ 770/* function to assist building execv() arguments */
531void 771void
532addargs(arglist *args, char *fmt, ...) 772addargs(arglist *args, char *fmt, ...)
@@ -1743,3 +1983,50 @@ child_set_env(char ***envp, u_int *envsizep, const char *name,
1743 snprintf(env[i], strlen(name) + 1 + strlen(value) + 1, "%s=%s", name, value); 1983 snprintf(env[i], strlen(name) + 1 + strlen(value) + 1, "%s=%s", name, value);
1744} 1984}
1745 1985
1986/*
1987 * Check and optionally lowercase a domain name, also removes trailing '.'
1988 * Returns 1 on success and 0 on failure, storing an error message in errstr.
1989 */
1990int
1991valid_domain(char *name, int makelower, const char **errstr)
1992{
1993 size_t i, l = strlen(name);
1994 u_char c, last = '\0';
1995 static char errbuf[256];
1996
1997 if (l == 0) {
1998 strlcpy(errbuf, "empty domain name", sizeof(errbuf));
1999 goto bad;
2000 }
2001 if (!isalpha((u_char)name[0]) && !isdigit((u_char)name[0])) {
2002 snprintf(errbuf, sizeof(errbuf), "domain name \"%.100s\" "
2003 "starts with invalid character", name);
2004 goto bad;
2005 }
2006 for (i = 0; i < l; i++) {
2007 c = tolower((u_char)name[i]);
2008 if (makelower)
2009 name[i] = (char)c;
2010 if (last == '.' && c == '.') {
2011 snprintf(errbuf, sizeof(errbuf), "domain name "
2012 "\"%.100s\" contains consecutive separators", name);
2013 goto bad;
2014 }
2015 if (c != '.' && c != '-' && !isalnum(c) &&
2016 c != '_') /* technically invalid, but common */ {
2017 snprintf(errbuf, sizeof(errbuf), "domain name "
2018 "\"%.100s\" contains invalid characters", name);
2019 goto bad;
2020 }
2021 last = c;
2022 }
2023 if (name[l - 1] == '.')
2024 name[l - 1] = '\0';
2025 if (errstr != NULL)
2026 *errstr = NULL;
2027 return 1;
2028bad:
2029 if (errstr != NULL)
2030 *errstr = errbuf;
2031 return 0;
2032}