keystore UPDATE use keys stored in keystore module

Keystore is a module, which stores asymmetric and symmetric
keys. This commit implements using asymmetric keys stored in the
keystore for authentication over SSH.
diff --git a/src/config_server.c b/src/config_server.c
index d0aaeda..2c41ee0 100644
--- a/src/config_server.c
+++ b/src/config_server.c
@@ -312,12 +312,6 @@
 }
 
 static void
-nc_server_del_keystore_reference(struct nc_hostkey *hostkey)
-{
-    hostkey->keystore = NULL;
-}
-
-static void
 nc_server_del_auth_client_username(struct nc_client_auth *auth_client)
 {
     free(auth_client->username);
@@ -388,8 +382,6 @@
     if (hostkey->ks_type == NC_STORE_LOCAL) {
         nc_server_del_public_key(hostkey);
         nc_server_del_private_key(hostkey);
-    } else if (hostkey->ks_type == NC_STORE_KEYSTORE) {
-        nc_server_del_keystore_reference(hostkey);
     }
 
     nc_server_del_hostkey_name(hostkey);
@@ -486,6 +478,38 @@
     }
 }
 
+void
+nc_server_config_del_keystore(void)
+{
+    int i, j;
+    struct nc_keystore *ks = &server_opts.keystore;
+
+    /* delete all asymmetric keys */
+    for (i = 0; i < ks->asym_key_count; i++) {
+        free(ks->asym_keys[i].name);
+        free(ks->asym_keys[i].pub_base64);
+        free(ks->asym_keys[i].priv_base64);
+
+        for (j = 0; j < ks->asym_keys[i].cert_count; j++) {
+            /* free associated certificates */
+            free(ks->asym_keys[i].certs[j].name);
+            free(ks->asym_keys[i].certs[j].cert_base64);
+        }
+        free(ks->asym_keys[i].certs);
+        ks->asym_keys[i].cert_count = 0;
+    }
+    free(ks->asym_keys);
+    ks->asym_key_count = 0;
+
+    /* delete all symmetric keys */
+    for (i = 0; i < ks->sym_key_count; i++) {
+        free(ks->sym_keys[i].name);
+        free(ks->sym_keys[i].base64);
+    }
+    free(ks->sym_keys);
+    ks->sym_key_count = 0;
+}
+
 /* presence container */
 int
 nc_server_configure_listen(NC_OPERATION op)
@@ -1136,22 +1160,21 @@
 nc_server_create_keystore_reference(const struct lyd_node *node, struct nc_hostkey *hostkey)
 {
     uint16_t i;
-    struct nc_keystore *ks = NULL;
+    struct nc_keystore *ks = &server_opts.keystore;
 
     /* lookup name */
-    for (i = 0; i < server_opts.keystore_count; i++) {
-        if (!strcmp(lyd_get_value(node), server_opts.keystore[i].name)) {
-            ks = &server_opts.keystore[i];
+    for (i = 0; i < ks->asym_key_count; i++) {
+        if (!strcmp(lyd_get_value(node), ks->asym_keys[i].name)) {
             break;
         }
     }
 
-    if (!ks) {
-        ERR(NULL, "Keystore (%s) not found.", lyd_get_value(node));
+    if (i == ks->asym_key_count) {
+        ERR(NULL, "Keystore \"%s\" not found.", lyd_get_value(node));
         return 1;
     }
 
-    hostkey->keystore = ks;
+    hostkey->ks_ref = &ks->asym_keys[i];
 
     return 0;
 }
@@ -1166,7 +1189,7 @@
 
     assert(!strcmp(LYD_NAME(node), "keystore-reference"));
 
-    if ((equal_parent_name(node, 4, "server-identity")) && (equal_parent_name(node, 7, "listen"))) {
+    if ((equal_parent_name(node, 3, "server-identity")) && (equal_parent_name(node, 7, "listen"))) {
         if (nc_server_get_endpt(node, &endpt, NULL)) {
             ret = 1;
             goto cleanup;
@@ -1182,7 +1205,7 @@
                 goto cleanup;
             }
         } else {
-            hostkey->keystore = NULL;
+            hostkey->ks_ref = NULL;
         }
     }
 
@@ -2073,55 +2096,210 @@
 }
 
 static int
-nc_server_configure_certificates(const struct lyd_node *node, struct nc_keystore *ks)
+nc_server_configure_asymmetric_key_certificate(const struct lyd_node *tree, struct nc_ks_asym_key *key)
 {
     int ret = 0;
-    uint16_t cert_count;
+    struct lyd_node *node;
     void *tmp;
 
-    node = node->next;
-    if ((!node) || (strcmp(LYD_NAME(node), "certificate"))) {
-        WRN(NULL, "Certificates container is empty");
+    /* create new certificate */
+    tmp = realloc(key->certs, (key->cert_count + 1) * sizeof *key->certs);
+    if (!tmp) {
+        ERRMEM;
+        ret = 1;
+        goto cleanup;
+    }
+    key->certs = tmp;
+    key->cert_count++;
+
+    /* set name */
+    lyd_find_path(tree, "name", 0, &node);
+    assert(node);
+
+    key->certs[key->cert_count - 1].name = strdup(lyd_get_value(node));
+    if (!key->certs[key->cert_count - 1].name) {
+        ERRMEM;
+        ret = 1;
         goto cleanup;
     }
 
-    /* certificate list */
-    while (node) {
-        cert_count = ks->cert_count;
-        tmp = realloc(ks->certs, cert_count + 1);
-        if (!tmp) {
-            ERRMEM;
-            ret = 1;
-            goto cleanup;
-        }
-        ks->certs = tmp;
+    /* set certificate data */
+    lyd_find_path(tree, "cert-data", 0, &node);
+    assert(node);
 
-        ks->certs[cert_count].name = strdup(lyd_get_value(lyd_child(node)));
-        if (!ks->certs[cert_count].name) {
-            ERRMEM;
-            ret = 1;
-            goto cleanup;
-        }
-
-        ks->certs[cert_count].cert_data = strdup(lyd_get_value(lyd_child(node)->next));
-        if (!ks->certs[cert_count].cert_data) {
-            ERRMEM;
-            free(ks->certs[cert_count].name);
-            ret = 1;
-            goto cleanup;
-        }
-
-        ks->cert_count++;
+    key->certs[key->cert_count - 1].cert_base64 = strdup(lyd_get_value(node));
+    if (!key->certs[key->cert_count - 1].cert_base64) {
+        ERRMEM;
+        ret = 1;
+        goto cleanup;
     }
 
 cleanup:
-    if (ret) {
-        for (cert_count = 0; cert_count < ks->cert_count; cert_count++) {
-            free(ks->certs[cert_count].name);
-            free(ks->certs[cert_count].cert_data);
-        }
-        free(ks->certs);
+    return ret;
+}
+
+static int
+nc_server_configure_asymmetric_key(const struct lyd_node *tree)
+{
+    int ret = 0;
+    struct lyd_node *node = NULL, *iter;
+    void *tmp;
+    struct nc_keystore *ks = &server_opts.keystore;
+    struct nc_ks_asym_key *key;
+    const char *format;
+
+    /* create new asymmetric key */
+    tmp = realloc(ks->asym_keys, (ks->asym_key_count + 1) * sizeof *ks->asym_keys);
+    if (!tmp) {
+        ERRMEM;
+        ret = 1;
+        goto cleanup;
     }
+    ks->asym_keys = tmp;
+    memset(&ks->asym_keys[ks->asym_key_count], 0, sizeof *ks->asym_keys);
+    key = &ks->asym_keys[ks->asym_key_count];
+    ks->asym_key_count++;
+
+    /* set name */
+    lyd_find_path(tree, "name", 0, &node);
+    assert(node);
+
+    key->name = strdup(lyd_get_value(node));
+    if (!key->name) {
+        ERRMEM;
+        ret = 1;
+        goto cleanup;
+    }
+
+    /* set public-key-format, mandatory */
+    lyd_find_path(tree, "public-key-format", 0, &node);
+    assert(node);
+
+    format = ((struct lyd_node_term *)node)->value.ident->name;
+    if (!strcmp(format, "ssh-public-key-format")) {
+        key->pubkey_type = NC_SSH_PUBKEY_X509;
+    } else if (!strcmp(format, "subject-public-key-info-format")) {
+        key->pubkey_type = NC_SSH_PUBKEY_SSH2;
+    } else {
+        ERR(NULL, "Public key format \"%s\" not supported.", format);
+        ret = 1;
+        goto cleanup;
+    }
+
+    /* set public-key, mandatory */
+    lyd_find_path(tree, "public-key", 0, &node);
+    assert(node);
+
+    key->pub_base64 = strdup(lyd_get_value(node));
+    if (!key->pub_base64) {
+        ERRMEM;
+        ret = 1;
+        goto cleanup;
+    }
+
+    /* set private-key-format */
+    ret = lyd_find_path(tree, "private-key-format", 0, &node);
+    if (!ret) {
+        format = ((struct lyd_node_term *)node)->value.ident->name;
+        if (!strcmp(format, "rsa-private-key-format")) {
+            key->privkey_type = NC_SSH_KEY_RSA;
+        } else if (!strcmp(format, "ec-private-key-format")) {
+            key->privkey_type = NC_SSH_KEY_ECDSA;
+        } else {
+            ERR(NULL, "Private key format (%s) not supported.", format);
+            ret = 1;
+            goto cleanup;
+        }
+    }
+
+    /* set private key, mandatory */
+    lyd_find_path(tree, "cleartext-private-key", 0, &node);
+    assert(node);
+
+    key->priv_base64 = strdup(lyd_get_value(node));
+    if (!key->priv_base64) {
+        ERRMEM;
+        ret = 1;
+        goto cleanup;
+    }
+
+    /* set certificates associated with the key pair */
+    ret = lyd_find_path(tree, "certificates", 0, &node);
+    if (!ret) {
+        node = lyd_child(node);
+        if (node) {
+            /* certificate list instance */
+            LY_LIST_FOR(node, iter) {
+                if (nc_server_configure_asymmetric_key_certificate(iter, key)) {
+                    ret = 1;
+                    goto cleanup;
+                }
+            }
+        }
+    } else if (ret == LY_ENOTFOUND) {
+        /* certificates container not present, but it's ok */
+        ret = 0;
+    }
+
+cleanup:
+    return ret;
+}
+
+static int
+nc_server_configure_symmetric_key(const struct lyd_node *tree)
+{
+    int ret = 0;
+    const char *format;
+    struct lyd_node *node;
+    struct nc_keystore *ks = &server_opts.keystore;
+    struct nc_ks_sym_key *key;
+    void *tmp;
+
+    /* create new symmetric key */
+    tmp = realloc(ks->sym_keys, (ks->sym_key_count + 1) * sizeof *ks->sym_keys);
+    if (tmp) {
+        ERRMEM;
+        ret = 1;
+        goto cleanup;
+    }
+    memset(&ks->sym_keys[ks->sym_key_count], 0, sizeof *ks->sym_keys);
+    ks->sym_keys = tmp;
+    key = &ks->sym_keys[ks->sym_key_count];
+    ks->sym_key_count++;
+
+    /* set name */
+    lyd_find_path(tree, "name", 0, &node);
+    assert(node);
+
+    key->name = strdup(lyd_get_value(node));
+    if (!key->name) {
+        ERRMEM;
+        ret = 1;
+        goto cleanup;
+    }
+
+    /* check if the identity matches with the supported one */
+    lyd_find_path(tree, "key-format", 0, &node);
+    assert(node);
+
+    format = ((struct lyd_node_term *)node)->value.ident->name;
+    if (strcmp(format, "symmetric-key-format")) {
+        ret = 1;
+        goto cleanup;
+    }
+
+    /* set key data */
+    lyd_find_path(tree, "cleartext-key", 0, &node);
+    assert(node);
+
+    key->base64 = strdup(lyd_get_value(node));
+    if (!key->base64) {
+        ERRMEM;
+        ret = 1;
+        goto cleanup;
+    }
+
+cleanup:
     return ret;
 }
 
@@ -2130,99 +2308,51 @@
 {
     int ret = 0;
     uint32_t prev_lo;
-    struct lyd_node *tree, *node, *iter, *iter_tmp;
-    void *tmp;
-    struct nc_keystore *ks;
+    struct lyd_node *tree, *as_keys, *s_keys, *iter;
 
-    /* silently search for keystore node */
+    /* silently search for nodes, some of them may not be present */
     prev_lo = ly_log_options(0);
-    ret = lyd_find_path(data, "/ks:keystore", 0, &tree);
-    ly_log_options(prev_lo);
+
+    ret = lyd_find_path(data, "/ietf-keystore:keystore", 0, &tree);
     if (ret) {
         WRN(NULL, "Keystore container not found in the YANG data.");
-        return 0;
+        goto cleanup;
     }
 
-    /* asymmetric keys container */
-    lyd_find_path(tree, "asymmetric-keys", 0, (struct lyd_node **)&node);
-    if (!node) {
-        WRN(NULL, "Asymmetric keys container not found in the YANG data.");
-        return 0;
-    }
-
-    /* asymmetric key list */
-    lyd_find_path(node, "asymmetric-key", 0, (struct lyd_node **)&node);
-    if (!node) {
-        WRN(NULL, "Asymmetric keys container is empty.");
-        return 0;
-    }
-
-    LY_LIST_FOR(node, iter) {
-        tmp = realloc(server_opts.keystore, server_opts.keystore_count + 1);
-        if (!tmp) {
-            ERRMEM;
-            goto fail;
-        }
-        server_opts.keystore = tmp;
-        ks = &server_opts.keystore[server_opts.keystore_count];
-
-        iter_tmp = iter;
-        /* name */
-        iter_tmp = lyd_child(iter_tmp);
-        ks->name = strdup(lyd_get_value(iter_tmp));
-        if (!ks->name) {
-            ERRMEM;
-            goto fail;
-        }
-
-        /* mandatory public-key-format */
-        iter_tmp = iter_tmp->next;
-        if (nc_server_configure_public_key_format(iter_tmp, 0)) {
-            free(ks->name);
-            goto fail;
-        }
-
-        /* mandatory public-key */
-        iter_tmp = iter_tmp->next;
-        ks->pub_base64 = strdup(lyd_get_value(iter_tmp));
-        if (!ks->pub_base64) {
-            free(ks->name);
-            ERRMEM;
-            goto fail;
-        }
-
-        iter_tmp = iter_tmp->next;
-        while (iter_tmp) {
-            if (!strcmp(LYD_NAME(iter_tmp), "private-key-format")) {
-                if (nc_server_configure_private_key_format(iter_tmp, 0)) {
-                    goto fail;
-                }
-            } else if (!strcmp(LYD_NAME(iter_tmp), "private-key-type")) {
-                if ((!strcmp(LYD_NAME(lyd_child(iter_tmp)), "cleartext-private-key")) &&
-                        (!strcmp(LYD_NAME(lyd_child(lyd_child(iter_tmp))), "cleartext-private-key"))) {
-                    ks->priv_base64 = strdup(lyd_get_value(lyd_child(lyd_child(iter_tmp))));
-                    if (!ks->priv_base64) {
-                        ERRMEM;
-                        goto fail;
-                    }
-                }
-            } else if (!strcmp(LYD_NAME(iter_tmp), "certificates")) {
-                if (nc_server_configure_certificates(iter_tmp, ks)) {
-                    goto fail;
+    ret = lyd_find_path(tree, "asymmetric-keys", 0, &as_keys);
+    if (!ret) {
+        /* asymmetric keys container is present */
+        as_keys = lyd_child(as_keys);
+        if (as_keys && !strcmp(LYD_NAME(as_keys), "asymmetric-key")) {
+            /* asymmetric key list */
+            LY_LIST_FOR(as_keys, iter) {
+                if (nc_server_configure_asymmetric_key(iter)) {
+                    ret = 1;
+                    goto cleanup;
                 }
             }
-            /* todo CSR? */
-            iter_tmp = iter_tmp->next;
         }
-
-        server_opts.keystore_count++;
     }
 
-    return 0;
+    ret = lyd_find_path(tree, "symmetric-keys", 0, &s_keys);
+    if (!ret) {
+        /* symmetric keys container is present */
+        s_keys = lyd_child(s_keys);
+        if (s_keys && !strcmp(LYD_NAME(s_keys), "symmetric-key")) {
+            /* symmetric key list */
+            LY_LIST_FOR(s_keys, iter) {
+                if (nc_server_configure_symmetric_key(iter)) {
+                    ret = 1;
+                    goto cleanup;
+                }
+            }
+        }
+    }
 
-fail:
-    free(server_opts.keystore);
-    return 1;
+cleanup:
+    /* reset the logging options back to what they were */
+    ly_log_options(prev_lo);
+    return ret;
 }
 
 API int
diff --git a/src/config_server.h b/src/config_server.h
index 9adfede..9397f66 100644
--- a/src/config_server.h
+++ b/src/config_server.h
@@ -76,6 +76,11 @@
  */
 int nc_server_configure_listen(NC_OPERATION op);
 
+/**
+ * @brief Deletes every key stored in the keystore.
+ */
+void nc_server_config_del_keystore(void);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/session_p.h b/src/session_p.h
index 3db1457..6818ed1 100644
--- a/src/session_p.h
+++ b/src/session_p.h
@@ -107,13 +107,25 @@
 };
 
 struct nc_keystore {
-    char *name;
-    char *pub_base64;
-    char *priv_base64;
-    NC_SSH_KEY_TYPE privkey_type;
+    struct nc_ks_asym_key {
+        char *name;
+        NC_SSH_PUBKEY_TYPE pubkey_type;
+        char *pub_base64;
+        NC_SSH_KEY_TYPE privkey_type;
+        char *priv_base64;
+        struct {
+            char *name;
+            char *cert_base64;
+        } *certs;
+        uint16_t cert_count;
+    } *asym_keys;
+    uint16_t asym_key_count;
 
-    struct nc_certificate *certs;
-    uint16_t cert_count;
+    struct nc_ks_sym_key {
+        char *name;
+        char *base64;
+    } *sym_keys;
+    uint16_t sym_key_count;
 };
 
 struct nc_client_auth {
@@ -124,8 +136,8 @@
         struct {
             struct nc_client_auth_pubkey {
                 char *name;
-                char *pub_base64;
                 NC_SSH_PUBKEY_TYPE pubkey_type;
+                char *pub_base64;
             } *pubkeys;
             uint16_t pubkey_count;
         };
@@ -149,7 +161,7 @@
             NC_SSH_KEY_TYPE privkey_type;
             char *priv_base64;
         };
-        struct nc_keystore *keystore;
+        struct nc_ks_asym_key *ks_ref;
     };
 };
 
@@ -319,8 +331,7 @@
 #endif
 
     pthread_rwlock_t config_lock;
-    struct nc_keystore *keystore; /**< store for keys/certificates */
-    uint16_t keystore_count;
+    struct nc_keystore keystore; /**< store for keys/certificates */
 
     struct nc_bind *binds;
     struct nc_endpt {