diff --git a/dmtree/tr181/device.c b/dmtree/tr181/device.c index cb34f9a9..14a6e750 100644 --- a/dmtree/tr181/device.c +++ b/dmtree/tr181/device.c @@ -44,6 +44,7 @@ #ifdef BBF_TR104 #include "servicesvoiceservice.h" #endif +#include "ssh.h" /************************************************************* * GET & SET PARAM @@ -122,6 +123,7 @@ DMOBJ tDeviceObj[] = { #endif {"GatewayInfo", &DMREAD, NULL, NULL, NULL, NULL, NULL, NULL, NULL, tGatewayInfoParams, NULL, BBFDM_CWMP, NULL, "2.0"}, {"MQTT", &DMREAD, NULL, NULL, "file:/etc/config/mosquitto", NULL, NULL, NULL, tMQTTObj, tMQTTParams, NULL, BBFDM_BOTH, NULL, "2.10"}, +{"SSH", &DMREAD, NULL, NULL, "file:/etc/config/dropbear", NULL, NULL, NULL, tSSHObj, tSSHParams, NULL, BBFDM_BOTH, NULL, "2.16"}, {0} }; diff --git a/dmtree/tr181/ssh.c b/dmtree/tr181/ssh.c new file mode 100644 index 00000000..15923b87 --- /dev/null +++ b/dmtree/tr181/ssh.c @@ -0,0 +1,994 @@ +/* + * Copyright (C) 2023 iopsys Software Solutions AB + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * Author Suvendhu Hansa + * + */ + +#include "dmbbfcommon.h" +#include "ssh.h" + +#define DROPBEAR_KEY_FILE "/etc/dropbear/authorized_keys" + +struct ssh_session_args { + struct list_head list; + char ip[45]; + char port[6]; + char pid[15]; +}; + +struct dmmap_ssh +{ + struct list_head list; + struct uci_section *config_section; + struct uci_section *dmmap_section; + struct list_head *sessions; +}; + +static void add_pubkey(const char *cur, const char *new) +{ + if (DM_STRLEN(cur) == 0) { + if (DM_STRLEN(new) == 0) + return; + + FILE *fp = fopen(DROPBEAR_KEY_FILE, "a"); + if (fp != NULL) { + fputs(new, fp); + fclose(fp); + } + } else { + char line[5120] = {0}; + FILE *fp = fopen(DROPBEAR_KEY_FILE, "r"); + FILE *tmp = fopen("replace.tmp", "w"); + + if (fp == NULL || tmp == NULL) + return; + + while (fgets(line, sizeof(line), fp) != NULL) { + remove_new_line(line); + if (DM_STRCMP(line, cur) == 0 && DM_STRLEN(new) != 0) + fputs(new, tmp); + else + fputs(line, tmp); + } + + fclose(fp); + fclose(tmp); + + remove(DROPBEAR_KEY_FILE); + rename("replace.tmp", DROPBEAR_KEY_FILE); + } +} + +static void remove_pubkey(const char *key) +{ + if (DM_STRLEN(key) == 0) + return; + + char line[5120] = {0}; + FILE *fp = fopen(DROPBEAR_KEY_FILE, "r"); + FILE *tmp = fopen("replace.tmp", "w"); + + if (fp == NULL || tmp == NULL) + return; + + while (fgets(line, sizeof(line), fp) != NULL) { + remove_new_line(line); + if (DM_STRCMP(line, key) != 0) + fputs(line, tmp); + } + + fclose(fp); + fclose(tmp); + + remove(DROPBEAR_KEY_FILE); + rename("replace.tmp", DROPBEAR_KEY_FILE); +} + +static bool key_exist_in_keyfile(const char *key) +{ + if (DM_STRLEN(key) == 0) + return true; + + char line[5120] = {0}; + FILE *fp = fopen(DROPBEAR_KEY_FILE, "r"); + if (fp == NULL) + return false; + + bool ret = false; + while (fgets(line, sizeof(line), fp) != NULL) { + remove_new_line(line); + if (DM_STRCMP(line, key) == 0) { + ret = true; + break; + } + } + + fclose(fp); + return ret; +} + +static bool key_exists(const char *key) +{ + struct uci_section *s = NULL, *stmp = NULL; + bool exists = false; + + uci_path_foreach_sections_safe(bbfdm, "dmmap_dropbear", "authkey", stmp, s) { + char *val = NULL; + dmuci_get_value_by_section_string(s, "pubkey", &val); + if (DM_STRCMP(val, key) == 0) { + exists = true; + break; + } + } + + return exists; +} + +static void add_ssh_config_dup_list(struct list_head *dup_list, struct uci_section *config_section, struct uci_section *dmmap_section) +{ + struct dmmap_ssh *dmmap_config; + + dmmap_config = dmcalloc(1, sizeof(struct dmmap_ssh)); + list_add_tail(&dmmap_config->list, dup_list); + dmmap_config->config_section = config_section; + dmmap_config->dmmap_section = dmmap_section; +} + +static void free_ssh_config_dup_list(struct list_head *dup_list) +{ + struct dmmap_ssh *dmmap_config = NULL, *tmp = NULL; + + list_for_each_entry_safe(dmmap_config, tmp, dup_list, list) { + list_del(&dmmap_config->list); + DMFREE(dmmap_config); + } +} + +static void synchronize_ssh_config_sections_with_dmmap(char *package, char *section_type, char *dmmap_package, struct list_head *dup_list) +{ + struct uci_section *s, *stmp, *dmmap_sect; + char *v; + + uci_foreach_sections(package, section_type, s) { + /* + * create/update corresponding dmmap section that have same config_section link and using param_value_array + */ + if ((dmmap_sect = get_dup_section_in_dmmap(dmmap_package, section_type, section_name(s))) == NULL) { + dmuci_add_section_bbfdm(dmmap_package, section_type, &dmmap_sect); + dmuci_set_value_by_section_bbfdm(dmmap_sect, "section_name", section_name(s)); + } + + /* + * Add system and dmmap sections to the list + */ + add_ssh_config_dup_list(dup_list, s, dmmap_sect); + } + + /* + * Delete unused dmmap sections + */ + uci_path_foreach_sections_safe(bbfdm, dmmap_package, section_type, stmp, s) { + dmuci_get_value_by_section_string(s, "section_name", &v); + if (get_origin_section_from_config(package, section_type, v) == NULL) + dmuci_delete_by_section(s, NULL, NULL); + } +} + +static void ssh_server_session_init(struct list_head *sess_list) +{ + char cmd[512] = {0}; + + snprintf(cmd, sizeof(cmd), "netstat -ntp | grep dropbear | awk \'{print $5,$7}\' | cut -d\'/\' -f 1"); + FILE *pp = popen(cmd, "r"); + if (pp == NULL) + return; + + char line[256] = {0}; + while (fgets(line, sizeof(line), pp) != NULL) { + remove_new_line(line); + if (DM_STRLEN(line) == 0) + continue; + + char *port = NULL; + char *pid = NULL; + + char *chr = strchr(line, ':'); + if (chr) { + *chr = 0; + port = chr + 1; + } + + if (DM_STRLEN(port) != 0) { + char *sp = strchr(port, ' '); + if (sp) { + *sp = 0; + pid = sp + 1; + } + } + + struct ssh_session_args *session = dmcalloc(1, sizeof(struct ssh_session_args)); + if (session == NULL) + break; + + strncpy(session->ip, line, sizeof(session->ip)); + if (DM_STRLEN(port) != 0) { + snprintf(session->port, sizeof(session->port), "%s", port); + } + + if (DM_STRLEN(pid) != 0) { + snprintf(session->pid, sizeof(session->pid), "%s", pid); + } + + + list_add_tail(&session->list, sess_list); + } + + pclose(pp); +} + +static void free_ssh_session_list(struct list_head *sess_list) +{ + struct ssh_session_args *session = NULL, *tmp = NULL; + list_for_each_entry_safe(session, tmp, sess_list, list) { + list_del(&session->list); + DMFREE(session); + } +} + +static void close_active_sessions(struct uci_section *s) +{ + bool b; + char *value = dmuci_get_value_by_section_fallback_def(s, "enable", "1"); + string_to_bool(value, &b); + if (!b) + return; + + char pid_file[125] = {0}; + char *sec_name = section_name(s); + if (DM_STRLEN(sec_name) == 0) + return; + + snprintf(pid_file, sizeof(pid_file), "/var/run/dropbear.%s.pid", sec_name); + if (DM_STRLEN(pid_file) == 0) + return; + + FILE *fp = fopen(pid_file, "r"); + if (fp == NULL) + return; + + unsigned int pid; + if (fscanf(fp, "%u", &pid) != 1) { + fclose(fp); + return; + } + + fclose(fp); + char cmd[512] = {0}; + snprintf(cmd, sizeof(cmd), "ps -w | grep %s | grep -v grep | awk \'{print $1}\'", pid_file); + FILE *pp = popen(cmd, "r"); + if (pp == NULL) + return; + + char line[15] = {0}; + while (fgets(line, sizeof(line), pp) != NULL) { + remove_new_line(line); + if (DM_STRLEN(line) == 0) + continue; + + if (strtoul(line, NULL, 10) == pid) + continue; + + snprintf(cmd, sizeof(cmd), "kill -15 %s", line); + system(cmd); + } + + pclose(pp); +} + +/************************************************************* + * ADD & DEL OBJ + *************************************************************/ +static int addObjSSHServer(char *refparam, struct dmctx *ctx, void *data, char **instance) +{ + struct uci_section *s = NULL, *dmmap_s = NULL; + char s_name[16]; + + snprintf(s_name, sizeof(s_name), "server_%s", *instance); + + dmuci_add_section("dropbear", "dropbear", &s); + dmuci_rename_section_by_section(s, s_name); + dmuci_set_value_by_section(s, "enable", "0"); + dmuci_set_value_by_section(s, "Port", "22"); + dmuci_set_value_by_section(s, "IdleTimeout", "180"); + dmuci_set_value_by_section(s, "SSHKeepAlive", "300"); + dmuci_set_value_by_section(s, "RootLogin", "0"); + dmuci_set_value_by_section(s, "PasswordAuth", "0"); + dmuci_set_value_by_section(s, "RootPasswordAuth", "0"); + dmuci_set_value_by_section(s, "MaxAuthTries", "3"); + + dmuci_add_section_bbfdm("dmmap_dropbear", "dropbear", &dmmap_s); + dmuci_set_value_by_section(dmmap_s, "section_name", s_name); + dmuci_set_value_by_section(dmmap_s, "server_instance", *instance); + return 0; +} + +static int delObjSSHServer(char *refparam, struct dmctx *ctx, void *data, char *instance, unsigned char del_action) +{ + struct uci_section *s = NULL, *stmp = NULL; + + switch (del_action) { + case DEL_INST: + close_active_sessions(((struct dmmap_ssh *)data)->config_section); + dmuci_delete_by_section(((struct dmmap_ssh *)data)->config_section, NULL, NULL); + dmuci_delete_by_section(((struct dmmap_ssh *)data)->dmmap_section, NULL, NULL); + break; + case DEL_ALL: + uci_foreach_sections_safe("dropbear", "dropbear", stmp, s) { + struct uci_section *dmmap_section = NULL; + get_dmmap_section_of_config_section("dmmap_dropbear", "dropbear", section_name(s), &dmmap_section); + close_active_sessions(s); + dmuci_delete_by_section(s, NULL, NULL); + dmuci_delete_by_section(dmmap_section, NULL, NULL); + } + break; + } + return 0; +} + +static int addObjSSHKey(char *refparam, struct dmctx *ctx, void *data, char **instance) +{ + struct uci_section *s = NULL; + + dmuci_add_section_bbfdm("dmmap_dropbear", "authkey", &s); + return 0; +} + +static int delObjSSHKey(char *refparam, struct dmctx *ctx, void *data, char *instance, unsigned char del_action) +{ + struct uci_section *s = NULL, *stmp = NULL; + char *value = NULL; + + switch (del_action) { + case DEL_INST: + dmuci_get_value_by_section_string((struct uci_section *)data, "pubkey", &value); + remove_pubkey(value); + dmuci_delete_by_section((struct uci_section *)data, NULL, NULL); + break; + case DEL_ALL: + uci_path_foreach_sections_safe(bbfdm, "dmmap_dropbear", "authkey", stmp, s) { + dmuci_get_value_by_section_string(s, "pubkey", &value); + remove_pubkey(value); + dmuci_delete_by_section(s, NULL, NULL); + } + break; + } + return 0; +} + +/************************************************************* + * ENTRY METHODS + *************************************************************/ +static int browseSSHServerInst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance) +{ + struct dmmap_ssh *p = NULL; + LIST_HEAD(dup_list); + LIST_HEAD(session_list); + char *inst = NULL; + + ssh_server_session_init(&session_list); + synchronize_ssh_config_sections_with_dmmap("dropbear", "dropbear", "dmmap_dropbear", &dup_list); + list_for_each_entry(p, &dup_list, list) { + bool b; + char *enable = dmuci_get_value_by_section_fallback_def(p->config_section, "enable", "1"); + char *act_date = dmuci_get_value_by_section_fallback_def(p->dmmap_section, "activationdate", ""); + + string_to_bool(enable, &b); + if (b && DM_STRLEN(act_date) == 0) { + char *tm = NULL; + dm_time_format(time(NULL), &tm); + dmuci_set_value_by_section(p->dmmap_section, "activationdate", tm); + } + + p->sessions = &session_list; + + inst = handle_instance(dmctx, parent_node, p->dmmap_section, "server_instance", "server_alias"); + + if (DM_LINK_INST_OBJ(dmctx, parent_node, (void *)p, inst) == DM_STOP) + break; + } + free_ssh_session_list(&session_list); + free_ssh_config_dup_list(&dup_list); + return 0; +} + +static int browseSSHKeyInst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance) +{ + struct uci_section *s = NULL, *stmp = NULL; + char *inst = NULL; + char line[5120] = {0}; + + /* create entries for the keys available in DROPBEAR_KEY_FILE but not in dmmap */ + FILE *fp = fopen(DROPBEAR_KEY_FILE, "r"); + if (fp != NULL) { + while (fgets(line, sizeof(line), fp) != NULL) { + struct uci_section *dmmap_sect = NULL; + remove_new_line(line); + bool exists = false; + + uci_path_foreach_sections_safe(bbfdm, "dmmap_dropbear", "authkey", stmp, s) { + char *val = NULL; + dmuci_get_value_by_section_string(s, "pubkey", &val); + if (DM_STRCMP(val, line) == 0) { + exists = true; + break; + } + } + + if (!exists) { + dmmap_sect = NULL; + dmuci_add_section_bbfdm("dmmap_dropbear", "authkey", &dmmap_sect); + dmuci_set_value_by_section(dmmap_sect, "pubkey", line); + } + } + + fclose(fp); + } + + /* delete keys from dmmap which are not available in DROPBEAR_KEY_FILE */ + stmp = NULL; + s = NULL; + uci_path_foreach_sections_safe(bbfdm, "dmmap_dropbear", "authkey", stmp, s) { + char *val = NULL; + dmuci_get_value_by_section_string(s, "pubkey", &val); + if (!key_exist_in_keyfile(val)) { + dmuci_delete_by_section(s, NULL, NULL); + } + } + + /* enlist objects */ + stmp = NULL; + s = NULL; + uci_path_foreach_sections_safe(bbfdm, "dmmap_dropbear", "authkey", stmp, s) { + inst = handle_instance(dmctx, parent_node, s, "instance", "alias"); + + if (DM_LINK_INST_OBJ(dmctx, parent_node, (void *)s, inst) == DM_STOP) + break; + } + return 0; +} + +static int browseSSHServerSessionInst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance) +{ + bool b; + char *inst = NULL; + int id = 0; + + struct uci_section *s = ((struct dmmap_ssh *)prev_data)->config_section; + char *value = dmuci_get_value_by_section_fallback_def(s, "enable", "1"); + string_to_bool(value, &b); + if (!b) + return 0; + + char pid_file[125] = {0}; + char *sec_name = section_name(s); + if (DM_STRLEN(sec_name) == 0) + return 0; + + snprintf(pid_file, sizeof(pid_file), "/var/run/dropbear.%s.pid", sec_name); + if (DM_STRLEN(pid_file) == 0) + return 0; + + FILE *fp = fopen(pid_file, "r"); + if (fp == NULL) + return 0; + + unsigned int pid; + if (fscanf(fp, "%u", &pid) != 1) { + fclose(fp); + return 0; + } + + fclose(fp); + char cmd[512] = {0}; + snprintf(cmd, sizeof(cmd), "ps -w | grep %s | grep -v grep | awk \'{print $1}\'", pid_file); + FILE *pp = popen(cmd, "r"); + if (pp == NULL) { + return 0; + } + + char line[15] = {0}; + while (fgets(line, sizeof(line), pp) != NULL) { + remove_new_line(line); + if (DM_STRLEN(line) == 0) + continue; + + if (strtoul(line, NULL, 10) == pid) { + continue; + } + + struct ssh_session_args *session = NULL; + struct list_head *sess_list = ((struct dmmap_ssh *)prev_data)->sessions; + bool found = false; + list_for_each_entry(session, sess_list, list) { + if (DM_STRCMP(session->pid, line) == 0) { + found = true; + break; + } + } + + if (found) { + inst = handle_instance_without_section(dmctx, parent_node, ++id); + if (DM_LINK_INST_OBJ(dmctx, parent_node, (void *)session, inst) == DM_STOP) + break; + } + } + + pclose(pp); + return 0; +} + +/************************************************************* +* GET & SET PARAM +**************************************************************/ +static int get_ssh_server_num(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) +{ + int cnt = get_number_of_entries(ctx, data, instance, browseSSHServerInst); + dmasprintf(value, "%d", cnt); + return 0; +} + +static int get_ssh_key_num(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) +{ + int cnt = get_number_of_entries(ctx, data, instance, browseSSHKeyInst); + dmasprintf(value, "%d", cnt); + return 0; +} + +static int get_ssh_status(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) +{ + *value = "Enabled"; + return 0; +} + +static int get_ssh_server_session_num(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) +{ + int cnt = get_number_of_entries(ctx, data, instance, browseSSHServerSessionInst); + dmasprintf(value, "%d", cnt); + return 0; +} + +static int get_ssh_server_enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) +{ + *value = dmuci_get_value_by_section_fallback_def(((struct dmmap_ssh *)data)->config_section, "enable", "1"); + return 0; +} + +static int set_ssh_server_enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action) +{ + bool b; + + switch (action) { + case VALUECHECK: + if (dm_validate_boolean(value)) + return FAULT_9007; + break; + case VALUESET: + string_to_bool(value, &b); + char *cur = dmuci_get_value_by_section_fallback_def(((struct dmmap_ssh *)data)->config_section, "enable", "1"); + bool cur_val; + string_to_bool(cur, &cur_val); + + if (b == cur_val) + break; + + if (b) { + dmuci_set_value_by_section(((struct dmmap_ssh *)data)->config_section, "enable", "1"); + char *tm = NULL; + dm_time_format(time(NULL), &tm); + dmuci_set_value_by_section(((struct dmmap_ssh *)data)->dmmap_section, "activationdate", tm); + } else { + close_active_sessions(((struct dmmap_ssh *)data)->config_section); + dmuci_set_value_by_section(((struct dmmap_ssh *)data)->config_section, "enable", "0"); + dmuci_set_value_by_section(((struct dmmap_ssh *)data)->dmmap_section, "activationdate", ""); + } + break; + } + return 0; +} + +static int get_ssh_server_alias(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) +{ + dmuci_get_value_by_section_string(((struct dmmap_ssh *)data)->dmmap_section, "server_alias", value); + if ((*value)[0] == '\0') + dmasprintf(value, "cpe-%s", instance); + return 0; +} + +static int set_ssh_server_alias(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action) +{ + switch (action) { + case VALUECHECK: + if (dm_validate_string(value, -1, 64, NULL, NULL)) + return FAULT_9007; + break; + case VALUESET: + dmuci_set_value_by_section(((struct dmmap_ssh *)data)->dmmap_section, "server_alias", value); + break; + } + return 0; +} + +static int get_ssh_server_interface(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) +{ + char *linker = NULL; + dmuci_get_value_by_section_string(((struct dmmap_ssh *)data)->config_section, "Interface", &linker); + if (DM_STRLEN(linker) != 0) + adm_entry_get_linker_param(ctx, "Device.IP.Interface.", linker, value); + + return 0; +} + +static int set_ssh_server_interface(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action) +{ + char *linker = NULL; + char *allowed_objects[] = {"Device.IP.Interface.", NULL}; + + switch (action) { + case VALUECHECK: + if (dm_validate_string(value, -1, 256, NULL, NULL)) + return FAULT_9007; + + if (dm_entry_validate_allowed_objects(ctx, value, allowed_objects)) + return FAULT_9007; + + break; + case VALUESET: + adm_entry_get_linker_value(ctx, value, &linker); + dmuci_set_value_by_section(((struct dmmap_ssh *)data)->config_section, "Interface", linker ? linker : ""); + break; + } + return 0; +} + +static int get_ssh_server_port(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) +{ + *value = dmuci_get_value_by_section_fallback_def(((struct dmmap_ssh *)data)->config_section, "Port", "22"); + return 0; +} + +static int set_ssh_server_port(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action) +{ + switch (action) { + case VALUECHECK: + if (dm_validate_unsignedInt(value, RANGE_ARGS{{"1","65535"}},1)) + return FAULT_9007; + break; + case VALUESET: + dmuci_set_value_by_section(((struct dmmap_ssh *)data)->config_section, "Port", value); + break; + } + return 0; +} + +static int get_ssh_server_idle(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) +{ + *value = dmuci_get_value_by_section_fallback_def(((struct dmmap_ssh *)data)->config_section, "IdleTimeout", "0"); + return 0; +} + +static int set_ssh_server_idle(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action) +{ + switch (action) { + case VALUECHECK: + if (dm_validate_unsignedInt(value, RANGE_ARGS{{NULL,NULL}},1)) + return FAULT_9007; + break; + case VALUESET: + dmuci_set_value_by_section(((struct dmmap_ssh *)data)->config_section, "IdleTimeout", value); + break; + } + return 0; +} + +static int get_ssh_server_keepalive(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) +{ + *value = dmuci_get_value_by_section_fallback_def(((struct dmmap_ssh *)data)->config_section, "SSHKeepAlive", "300"); + return 0; +} + +static int set_ssh_server_keepalive(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action) +{ + switch (action) { + case VALUECHECK: + if (dm_validate_unsignedInt(value, RANGE_ARGS{{NULL,NULL}},1)) + return FAULT_9007; + break; + case VALUESET: + dmuci_set_value_by_section(((struct dmmap_ssh *)data)->config_section, "SSHKeepAlive", value); + break; + } + return 0; +} + +static int get_ssh_server_rootlogin(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) +{ + *value = dmuci_get_value_by_section_fallback_def(((struct dmmap_ssh *)data)->config_section, "RootLogin", "1"); + return 0; +} + +static int set_ssh_server_rootlogin(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action) +{ + bool b; + + switch (action) { + case VALUECHECK: + if (dm_validate_boolean(value)) + return FAULT_9007; + break; + case VALUESET: + string_to_bool(value, &b); + dmuci_set_value_by_section(((struct dmmap_ssh *)data)->config_section, "RootLogin", b ? "1" : "0"); + break; + } + return 0; +} + +static int get_ssh_server_passwordlogin(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) +{ + *value = dmuci_get_value_by_section_fallback_def(((struct dmmap_ssh *)data)->config_section, "PasswordAuth", "1"); + return 0; +} + +static int set_ssh_server_passwordlogin(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action) +{ + bool b; + + switch (action) { + case VALUECHECK: + if (dm_validate_boolean(value)) + return FAULT_9007; + break; + case VALUESET: + string_to_bool(value, &b); + dmuci_set_value_by_section(((struct dmmap_ssh *)data)->config_section, "PasswordAuth", b ? "1" : "0"); + break; + } + return 0; +} + +static int get_ssh_server_rootpasswordlogin(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) +{ + *value = dmuci_get_value_by_section_fallback_def(((struct dmmap_ssh *)data)->config_section, "RootPasswordAuth", "1"); + return 0; +} + +static int set_ssh_server_rootpasswordlogin(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action) +{ + bool b; + + switch (action) { + case VALUECHECK: + if (dm_validate_boolean(value)) + return FAULT_9007; + break; + case VALUESET: + string_to_bool(value, &b); + dmuci_set_value_by_section(((struct dmmap_ssh *)data)->config_section, "RootPasswordAuth", b ? "1" : "0"); + break; + } + return 0; +} + +static int get_ssh_server_maxauthtries(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) +{ + *value = dmuci_get_value_by_section_fallback_def(((struct dmmap_ssh *)data)->config_section, "MaxAuthTries", "3"); + return 0; +} + +static int set_ssh_server_maxauthtries(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action) +{ + switch (action) { + case VALUECHECK: + if (dm_validate_unsignedInt(value, RANGE_ARGS{{NULL,NULL}},1)) + return FAULT_9007; + break; + case VALUESET: + dmuci_set_value_by_section(((struct dmmap_ssh *)data)->config_section, "MaxAuthTries", value); + break; + } + return 0; +} + +static int get_ssh_server_activationdate(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) +{ + dmuci_get_value_by_section_string(((struct dmmap_ssh *)data)->dmmap_section, "activationdate", value); + return 0; +} + +static int get_ssh_server_pid(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) +{ + bool b; + struct uci_section *s = ((struct dmmap_ssh *)data)->config_section; + char *en = dmuci_get_value_by_section_fallback_def(s, "enable", "1"); + + *value = "0"; + string_to_bool(en, &b); + if (!b) + return 0; + + char *sec_name = section_name(s); + if (DM_STRLEN(sec_name) == 0) + return 0; + + char pid_file[256] = {0}; + snprintf(pid_file, sizeof(pid_file), "/var/run/dropbear.%s.pid", sec_name); + if (DM_STRLEN(pid_file) == 0) + return 0; + + FILE *fp = fopen(pid_file, "r"); + if (fp == NULL) + return 0; + + unsigned int pid; + if (fscanf(fp, "%u", &pid) != 1) { + fclose(fp); + return 0; + } + + fclose(fp); + dmasprintf(value, "%u", pid); + return 0; +} + +static int get_ssh_server_session_ip(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) +{ + struct ssh_session_args *args = (struct ssh_session_args *)data; + *value = DM_STRLEN(args->ip) ? dmstrdup(args->ip) : ""; + return 0; +} + +static int get_ssh_server_session_port(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) +{ + struct ssh_session_args *args = (struct ssh_session_args *)data; + *value = DM_STRLEN(args->port) ? dmstrdup(args->port) : ""; + return 0; +} + +static int get_ssh_key_alias(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) +{ + dmuci_get_value_by_section_string((struct uci_section *)data, "alias", value); + if ((*value)[0] == '\0') + dmasprintf(value, "cpe-%s", instance); + return 0; +} + +static int set_ssh_key_alias(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action) +{ + switch (action) { + case VALUECHECK: + if (dm_validate_string(value, -1, 64, NULL, NULL)) + return FAULT_9007; + break; + case VALUESET: + dmuci_set_value_by_section((struct uci_section *)data, "alias", value); + break; + } + return 0; +} + +static int get_ssh_key_pubkey(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) +{ + dmuci_get_value_by_section_string((struct uci_section *)data, "pubkey", value); + return 0; +} + +static int set_ssh_key_pubkey(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action) +{ + char *cur_val = NULL; + + switch (action) { + case VALUECHECK: + if (dm_validate_string(value, -1, -1, NULL, NULL)) + return FAULT_9007; + + /* check if same as current key value */ + dmuci_get_value_by_section_string((struct uci_section *)data, "pubkey", &cur_val); + if (DM_STRCMP(cur_val, value) == 0) + break; + + if (key_exists(value)) + return FAULT_9001; + + break; + case VALUESET: + /* check if same as current key value then nothing to do */ + dmuci_get_value_by_section_string((struct uci_section *)data, "pubkey", &cur_val); + if (DM_STRCMP(cur_val, value) == 0) + break; + + add_pubkey(cur_val, value); + dmuci_set_value_by_section((struct uci_section *)data, "pubkey", value); + break; + } + return 0; +} + +static int operate_session_delete(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action) +{ + int ret = -1; + + struct ssh_session_args *args = (struct ssh_session_args *)data; + if (DM_STRLEN(args->pid) != 0) { + char cmd[128] = {0}; + snprintf(cmd, sizeof(cmd), "kill -15 %s", args->pid); + ret = system(cmd); + } + + return (ret != -1) ? CMD_SUCCESS : CMD_FAIL; +} + +/********************************************************************************************************************************** +* OBJ & LEAF DEFINITION +***********************************************************************************************************************************/ +/* *** Device.SSH. *** */ +DMOBJ tSSHObj[] = { +/* OBJ, permission, addobj, delobj, checkdep, browseinstobj, nextdynamicobj, dynamicleaf, nextobj, leaf, linker, bbfdm_type, uniqueKeys, version*/ +{"Server", &DMWRITE, addObjSSHServer, delObjSSHServer, NULL, browseSSHServerInst, NULL, NULL, tSSHServerObj, tSSHServerParams, NULL, BBFDM_BOTH, LIST_KEY{"Alias", NULL}, "2.16"}, +{"AuthorizedKey", &DMWRITE, addObjSSHKey, delObjSSHKey, NULL, browseSSHKeyInst, NULL, NULL, NULL, tSSHKeyParams, NULL, BBFDM_BOTH, LIST_KEY{"Alias", NULL}, "2.16"}, +{0} +}; + +DMOBJ tSSHServerObj[] = { +/* OBJ, permission, addobj, delobj, checkdep, browseinstobj, nextdynamicobj, dynamicleaf, nextobj, leaf, linker, bbfdm_type, uniqueKeys, version*/ +{"Session", &DMREAD, NULL, NULL, NULL, browseSSHServerSessionInst, NULL, NULL, NULL, tSSHServerSessionParams, NULL, BBFDM_BOTH, NULL, "2.16"}, +{0} +}; + +/* *** Device.SSH. *** */ +DMLEAF tSSHParams[] = { +/* PARAM, permission, type, getvalue, setvalue, bbfdm_type, version*/ +{"ServerNumberOfEntries", &DMREAD, DMT_UNINT, get_ssh_server_num, NULL, BBFDM_BOTH, "2.16"}, +{"AuthorizedKeyNumberOfEntries", &DMREAD, DMT_UNINT, get_ssh_key_num, NULL, BBFDM_BOTH, "2.16"}, +{"Status", &DMREAD, DMT_STRING, get_ssh_status, NULL, BBFDM_BOTH, "2.16"}, +{0} +}; + +/* *** Device.SSH.Server. *** */ +DMLEAF tSSHServerParams[] = { +/* PARAM, permission, type, getvalue, setvalue, bbfdm_type, version*/ +{"Enable", &DMWRITE, DMT_BOOL, get_ssh_server_enable, set_ssh_server_enable, BBFDM_BOTH, "2.16"}, +{"Alias", &DMWRITE, DMT_STRING, get_ssh_server_alias, set_ssh_server_alias, BBFDM_BOTH, "2.16"}, +{"Interface", &DMWRITE, DMT_STRING, get_ssh_server_interface, set_ssh_server_interface, BBFDM_BOTH, "2.16"}, +{"Port", &DMWRITE, DMT_UNINT, get_ssh_server_port, set_ssh_server_port, BBFDM_BOTH, "2.16"}, +{"IdleTimeout", &DMWRITE, DMT_UNINT, get_ssh_server_idle, set_ssh_server_idle, BBFDM_BOTH, "2.16"}, +{"KeepAlive", &DMWRITE, DMT_UNINT, get_ssh_server_keepalive, set_ssh_server_keepalive, BBFDM_BOTH, "2.16"}, +{"AllowRootLogin", &DMWRITE, DMT_BOOL, get_ssh_server_rootlogin, set_ssh_server_rootlogin, BBFDM_BOTH, "2.16"}, +{"AllowPasswordLogin", &DMWRITE, DMT_BOOL, get_ssh_server_passwordlogin, set_ssh_server_passwordlogin, BBFDM_BOTH, "2.16"}, +{"AllowRootPasswordLogin", &DMWRITE, DMT_BOOL, get_ssh_server_rootpasswordlogin, set_ssh_server_rootpasswordlogin, BBFDM_BOTH, "2.16"}, +{"MaxAuthTries", &DMWRITE, DMT_UNINT, get_ssh_server_maxauthtries, set_ssh_server_maxauthtries, BBFDM_BOTH, "2.16"}, +{"ActivationDate", &DMREAD, DMT_TIME, get_ssh_server_activationdate, NULL, BBFDM_BOTH, "2.16"}, +{"PID", &DMREAD, DMT_UNINT, get_ssh_server_pid, NULL, BBFDM_BOTH, "2.16"}, +{"SessionNumberOfEntries", &DMREAD, DMT_UNINT, get_ssh_server_session_num, NULL, BBFDM_BOTH, "2.16"}, +{0} +}; + +DMLEAF tSSHServerSessionParams[] = { +/* PARAM, permission, type, getvalue, setvalue, bbfdm_type, version*/ +{"IPAddress", &DMREAD, DMT_STRING, get_ssh_server_session_ip, NULL, BBFDM_BOTH, "2.16"}, +{"Port", &DMREAD, DMT_UNINT, get_ssh_server_session_port, NULL, BBFDM_BOTH, "2.16"}, +{"Delete()", &DMSYNC, DMT_COMMAND, NULL, operate_session_delete, BBFDM_USP, "2.16"}, +{0} +}; + +DMLEAF tSSHKeyParams[] = { +/* PARAM, permission, type, getvalue, setvalue, bbfdm_type, version*/ +{"Alias", &DMWRITE, DMT_STRING, get_ssh_key_alias, set_ssh_key_alias, BBFDM_BOTH, "2.16"}, +{"Key", &DMWRITE, DMT_STRING, get_ssh_key_pubkey, set_ssh_key_pubkey, BBFDM_BOTH, "2.16"}, +{0} +}; + diff --git a/dmtree/tr181/ssh.h b/dmtree/tr181/ssh.h new file mode 100644 index 00000000..fdc68787 --- /dev/null +++ b/dmtree/tr181/ssh.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2023 iopsys Software Solutions AB + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * Author Suvendhu Hansa + * + */ + +#ifndef __SSH_H +#define __SSH_H + +#include "libbbf_api/dmcommon.h" + +extern DMOBJ tSSHObj[]; +extern DMOBJ tSSHServerObj[]; +extern DMLEAF tSSHParams[]; +extern DMLEAF tSSHServerParams[]; +extern DMLEAF tSSHServerSessionParams[]; +extern DMLEAF tSSHKeyParams[]; + +#endif