From 79049981a513f9a10fac0f153e9b0b588326021f Mon Sep 17 00:00:00 2001
From: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
Date: Fri, 22 Feb 2019 13:06:49 +0100
Subject: [PATCH] config: Move common parser functions to config_parser.c

This will allow the moved functions to be used in the server side
configuration parser implementation.

Signed-off-by: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>

CVE:CVE-2023-6004
Upstream-Status: Backport [https://git.libssh.org/projects/libssh.git/commit/?id=79049981a513f9a10fac0f153e9b0b588326021f]
Signed-off-by: nikhil r <nikhil.r@kpit.com>
Comment: Removed 1 hunk from config.c as the function was intoduced in
later version

---
 include/libssh/config_parser.h |  57 ++++++++
 src/CMakeLists.txt             |   1 +
 src/config.c                   | 216 +-----------------------------
 src/config_parser.c            | 238 +++++++++++++++++++++++++++++++++
 4 files changed, 297 insertions(+), 215 deletions(-)
 create mode 100644 include/libssh/config_parser.h
 create mode 100644 src/config_parser.c

diff --git a/include/libssh/config_parser.h b/include/libssh/config_parser.h
new file mode 100644
index 00000000..e974917c
--- /dev/null
+++ b/include/libssh/config_parser.h
@@ -0,0 +1,57 @@
+/*
+ * config_parser.h - Common configuration file parser functions
+ *
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2019 by Red Hat, Inc.
+ *
+ * Author: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
+ *
+ * The SSH Library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at your
+ * option) any later version.
+ *
+ * The SSH Library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the SSH Library; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+#ifndef CONFIG_PARSER_H_
+#define CONFIG_PARSER_H_
+
+char *ssh_config_get_cmd(char **str);
+
+char *ssh_config_get_token(char **str);
+
+long ssh_config_get_long(char **str, long notfound);
+
+const char *ssh_config_get_str_tok(char **str, const char *def);
+
+int ssh_config_get_yesno(char **str, int notfound);
+
+/* @brief Parse SSH URI in format [user@]host[:port] from the given string
+ *
+ * @param[in]   tok      String to parse
+ * @param[out]  username Pointer to the location, where the new username will
+ *                       be stored or NULL if we do not care about the result.
+ * @param[out]  hostname Pointer to the location, where the new hostname will
+ *                       be stored or NULL if we do not care about the result.
+ * @param[out]  port     Pointer to the location, where the new port will
+ *                       be stored or NULL if we do not care about the result.
+ *
+ * @returns     SSH_OK if the provided string is in format of SSH URI,
+ *              SSH_ERROR on failure
+ */
+int ssh_config_parse_uri(const char *tok,
+        char **username,
+        char **hostname,
+        char **port);
+
+#endif /* LIBSSH_CONFIG_H_ */
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index fdb53baf..de66f056 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -163,6 +163,7 @@ set(libssh_SRCS
   external/poly1305.c
   external/sc25519.c
   chachapoly.c
+  config_parser.c
 )
 
 if (CMAKE_USE_PTHREADS_INIT)
diff --git a/src/config.c b/src/config.c
index 85ecd96a..4268545d 100644
--- a/src/config.c
+++ b/src/config.c
@@ -22,7 +22,7 @@
  */
 
 #include "config.h"
-
+#include "libssh/config_parser.h"
 #include <ctype.h>
 #include <stdio.h>
 #include <string.h>
@@ -228,102 +228,6 @@ static enum ssh_config_opcode_e ssh_config_get_opcode(char *keyword) {
   return SOC_UNKNOWN;
 }
 
-static char *ssh_config_get_cmd(char **str) {
-  register char *c;
-  char *r;
-
-  /* Ignore leading spaces */
-  for (c = *str; *c; c++) {
-    if (! isblank(*c)) {
-      break;
-    }
-  }
-
-  if (*c == '\"') {
-    for (r = ++c; *c; c++) {
-      if (*c == '\"') {
-        *c = '\0';
-        goto out;
-      }
-    }
-  }
-
-  for (r = c; *c; c++) {
-    if (*c == '\n') {
-      *c = '\0';
-      goto out;
-    }
-  }
-
-out:
-  *str = c + 1;
-
-  return r;
-}
-
-static char *ssh_config_get_token(char **str) {
-  register char *c;
-  char *r;
-
-  c = ssh_config_get_cmd(str);
-
-  for (r = c; *c; c++) {
-    if (isblank(*c) || *c == '=') {
-      *c = '\0';
-      goto out;
-    }
-  }
-
-out:
-  *str = c + 1;
-
-  return r;
-}
-
-static long ssh_config_get_long(char **str, long notfound) {
-  char *p, *endp;
-  long i;
-
-  p = ssh_config_get_token(str);
-  if (p && *p) {
-    i = strtol(p, &endp, 10);
-    if (p == endp) {
-      return notfound;
-    }
-    return i;
-  }
-
-  return notfound;
-}
-
-static const char *ssh_config_get_str_tok(char **str, const char *def) {
-  char *p;
-
-  p = ssh_config_get_token(str);
-  if (p && *p) {
-    return p;
-  }
-
-  return def;
-}
-
-static int ssh_config_get_yesno(char **str, int notfound) {
-  const char *p;
-
-  p = ssh_config_get_str_tok(str, NULL);
-  if (p == NULL) {
-    return notfound;
-  }
-
-  if (strncasecmp(p, "yes", 3) == 0) {
-    return 1;
-  } else if (strncasecmp(p, "no", 2) == 0) {
-    return 0;
-  }
-
-  return notfound;
-}
-
 static void local_parse_file(ssh_session session, const char *filename, int *parsing, int seen[]) {
   FILE *f;
   char line[MAX_LINE_SIZE] = {0};
diff --git a/src/config_parser.c b/src/config_parser.c
new file mode 100644
index 00000000..ae2aa2c8
--- /dev/null
+++ b/src/config_parser.c
@@ -0,0 +1,238 @@
+/*
+ * config_parser.c - Common configuration file parser functions
+ *
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2009-2013    by Andreas Schneider <asn@cryptomilk.org>
+ *
+ * The SSH Library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at your
+ * option) any later version.
+ *
+ * The SSH Library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the SSH Library; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "libssh/config_parser.h"
+#include "libssh/priv.h"
+
+char *ssh_config_get_cmd(char **str)
+{
+    register char *c;
+    char *r;
+
+    /* Ignore leading spaces */
+    for (c = *str; *c; c++) {
+        if (! isblank(*c)) {
+            break;
+        }
+    }
+
+    if (*c == '\"') {
+        for (r = ++c; *c; c++) {
+            if (*c == '\"') {
+                *c = '\0';
+                goto out;
+            }
+        }
+    }
+
+    for (r = c; *c; c++) {
+        if (*c == '\n') {
+            *c = '\0';
+            goto out;
+        }
+    }
+
+out:
+    *str = c + 1;
+
+    return r;
+}
+
+char *ssh_config_get_token(char **str)
+{
+    register char *c;
+    char *r;
+
+    c = ssh_config_get_cmd(str);
+
+    for (r = c; *c; c++) {
+        if (isblank(*c) || *c == '=') {
+            *c = '\0';
+            goto out;
+        }
+    }
+
+out:
+    *str = c + 1;
+
+    return r;
+}
+
+long ssh_config_get_long(char **str, long notfound)
+{
+    char *p, *endp;
+    long i;
+
+    p = ssh_config_get_token(str);
+    if (p && *p) {
+        i = strtol(p, &endp, 10);
+        if (p == endp) {
+            return notfound;
+        }
+        return i;
+    }
+
+    return notfound;
+}
+
+const char *ssh_config_get_str_tok(char **str, const char *def)
+{
+    char *p;
+
+    p = ssh_config_get_token(str);
+    if (p && *p) {
+        return p;
+    }
+
+    return def;
+}
+
+int ssh_config_get_yesno(char **str, int notfound)
+{
+    const char *p;
+
+    p = ssh_config_get_str_tok(str, NULL);
+    if (p == NULL) {
+        return notfound;
+    }
+
+    if (strncasecmp(p, "yes", 3) == 0) {
+        return 1;
+    } else if (strncasecmp(p, "no", 2) == 0) {
+        return 0;
+    }
+
+    return notfound;
+}
+
+int ssh_config_parse_uri(const char *tok,
+        char **username,
+        char **hostname,
+        char **port)
+{
+    char *endp = NULL;
+    long port_n;
+
+    /* Sanitize inputs */
+    if (username != NULL) {
+        *username = NULL;
+    }
+    if (hostname != NULL) {
+        *hostname = NULL;
+    }
+    if (port != NULL) {
+        *port = NULL;
+    }
+
+    /* Username part (optional) */
+    endp = strchr(tok, '@');
+    if (endp != NULL) {
+        /* Zero-length username is not valid */
+        if (tok == endp) {
+            goto error;
+        }
+        if (username != NULL) {
+            *username = strndup(tok, endp - tok);
+            if (*username == NULL) {
+                goto error;
+            }
+        }
+        tok = endp + 1;
+        /* If there is second @ character, this does not look like our URI */
+        endp = strchr(tok, '@');
+        if (endp != NULL) {
+            goto error;
+        }
+    }
+
+    /* Hostname */
+    if (*tok == '[') {
+        /* IPv6 address is enclosed with square brackets */
+        tok++;
+        endp = strchr(tok, ']');
+        if (endp == NULL) {
+            goto error;
+        }
+    } else {
+        /* Hostnames or aliases expand to the last colon or to the end */
+        endp = strrchr(tok, ':');
+        if (endp == NULL) {
+            endp = strchr(tok, '\0');
+        }
+    }
+    if (tok == endp) {
+        /* Zero-length hostnames are not valid */
+        goto error;
+    }
+    if (hostname != NULL) {
+        *hostname = strndup(tok, endp - tok);
+        if (*hostname == NULL) {
+            goto error;
+        }
+    }
+    /* Skip also the closing bracket */
+    if (*endp == ']') {
+        endp++;
+    }
+
+    /* Port (optional) */
+    if (*endp != '\0') {
+        char *port_end = NULL;
+
+        /* Verify the port is valid positive number */
+        port_n = strtol(endp + 1, &port_end, 10);
+        if (port_n < 1 || *port_end != '\0') {
+            SSH_LOG(SSH_LOG_WARN, "Failed to parse port number."
+                    " The value '%ld' is invalid or there are some"
+                    " trailing characters: '%s'", port_n, port_end);
+            goto error;
+        }
+        if (port != NULL) {
+            *port = strdup(endp + 1);
+            if (*port == NULL) {
+                goto error;
+            }
+        }
+    }
+
+    return SSH_OK;
+
+error:
+    if (username != NULL) {
+        SAFE_FREE(*username);
+    }
+    if (hostname != NULL) {
+        SAFE_FREE(*hostname);
+    }
+    if (port != NULL) {
+        SAFE_FREE(*port);
+    }
+    return SSH_ERROR;
+}
-- 
2.25.1

