1 /*************************************************************************
3 * Copyright 2020 highstreet technologies GmbH and others
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 ***************************************************************************/
20 #include "netconf_call_home.h"
21 #include "utils/log_utils.h"
22 #include "utils/sys_utils.h"
23 #include "utils/rand_utils.h"
24 #include "utils/http_client.h"
25 #include "utils/nts_utils.h"
29 #include "core/session.h"
30 #include "core/framework.h"
31 #include "core/xpath.h"
33 #define NETCONF_SSH_CALLHOME_CURL_SEND_PAYLOAD_FORMAT "{\"odl-netconf-callhome-server:device\":[{\"odl-netconf-callhome-server:unique-id\":\"%s\", \"odl-netconf-callhome-server:ssh-client-params\": {\"odl-netconf-callhome-server:host-key\":\"%s\",\"odl-netconf-callhome-server:credentials\":{\"odl-netconf-callhome-server:username\":\"netconf\",\"odl-netconf-callhome-server:passwords\":[\"netconf!\"]}}}]}"
34 #define NETCONF_TLS_CALLHOME_CURL_SEND_PAYLOAD_FORMAT "{\"odl-netconf-callhome-server:device\":[{\"odl-netconf-callhome-server:unique-id\":\"%s\", \"odl-netconf-callhome-server:tls-client-params\": {\"odl-netconf-callhome-server:certificate-id\":\"%s\",\"odl-netconf-callhome-server:key-id\":\"%s\"}}]}"
35 #define NETCONF_TRUSTED_CERTIFICATE_CURL_SEND_PAYLOAD_FORMAT "{\"input\":{\"trusted-certificate\":[{\"name\":\"%s\",\"certificate\":\"%s\"}]}}"
37 static int create_ssh_callhome_endpoint(sr_session_ctx_t *current_session, struct lyd_node *netconf_node);
38 static int create_tls_callhome_endpoint(sr_session_ctx_t *current_session, struct lyd_node *netconf_node);
39 static int send_odl_add_trusted_certificate(sr_session_ctx_t *current_session);
40 static int send_odl_callhome_configuration(sr_session_ctx_t *current_session, bool is_tls);
42 static int netconf_call_home_status = 0;
44 int netconf_call_home_feature_get_status(void) {
45 return netconf_call_home_status;
48 int netconf_call_home_feature_start(sr_session_ctx_t *current_session) {
49 assert(current_session);
54 bool callhome_enabled = false;
55 int rc = sr_get_item(current_session, NTS_NF_NETCONF_CALLHOME_ENABLED_SCHEMA_PATH, 0, &value);
57 callhome_enabled = value->data.bool_val;
61 // if value is not set yet, feature enable means we want to start call-home
62 if(strlen(framework_environment.nts.nf_standalone_start_features)) {
63 callhome_enabled = true;
67 if(callhome_enabled == false) {
68 log_add_verbose(2, "NETCONF CallHome is not enabled, not configuring NETCONF Server.\n");
72 struct lyd_node *netconf_node = 0;
73 netconf_node = lyd_new_path(NULL, session_context, IETF_NETCONF_SERVER_SCHEMA_XPATH, 0, 0, 0);
74 if(netconf_node == 0) {
75 log_error("could not create a new lyd_node\n");
76 return NTS_ERR_FAILED;
79 controller_details_t *controller = controller_details_get(current_session);
81 log_error("controller_details_get failed\n");
82 return NTS_ERR_FAILED;
85 if (controller->nc_callhome_port == 4335) {
86 // port is CallHome via TLS
87 rc = create_tls_callhome_endpoint(current_session, netconf_node);
88 if(rc != NTS_ERR_OK) {
89 log_error("could not create TLS CallHome endpoint on the NETCONF Server\n");
90 controller_details_free(controller);
91 return NTS_ERR_FAILED;
95 // port is CallHome via SSH
96 rc = create_ssh_callhome_endpoint(current_session, netconf_node);
97 if(rc != NTS_ERR_OK) {
98 log_error("could not create SSH CallHome endpoint on the NETCONF Server\n");
99 controller_details_free(controller);
100 return NTS_ERR_FAILED;
103 controller_details_free(controller);
105 rc = sr_edit_batch(current_session, netconf_node, "merge");
106 if(rc != SR_ERR_OK) {
107 log_error("could not edit batch on datastore\n");
108 return NTS_ERR_FAILED;
111 rc = sr_validate(current_session, IETF_NETCONF_SERVER_MODULE, 0);
112 if(rc != SR_ERR_OK) {
113 log_error("sr_validate issues on STARTUP\n");
114 return NTS_ERR_FAILED;
117 rc = sr_apply_changes(current_session, 0, 0);
118 if(rc != SR_ERR_OK) {
119 log_error("could not apply changes on datastore\n");
120 return NTS_ERR_FAILED;
123 lyd_free_withsiblings(netconf_node);
124 netconf_call_home_status = 1;
130 static int create_ssh_callhome_endpoint(sr_session_ctx_t *current_session, struct lyd_node *netconf_node) {
131 assert(current_session);
132 assert(netconf_node);
134 controller_details_t *controller = controller_details_get(current_session);
135 if(controller == 0) {
136 log_error("controller_details_get failed\n");
137 return NTS_ERR_FAILED;
140 char *controller_ip = strdup(controller->nc_callhome_ip);
141 uint16_t controller_callhome_port = controller->nc_callhome_port;
142 controller_details_free(controller);
144 if(controller_ip == 0) {
145 log_error("strdup failed\n");
146 return NTS_ERR_FAILED;
149 struct lyd_node *rcl = lyd_new_path(netconf_node, 0, IETF_NETCONF_SERVER_CH_SSH_TCP_CLIENT_SCHEMA_XPATH"/keepalives/idle-time", "1", 0, LYD_PATH_OPT_NOPARENTRET);
151 log_error("could not created yang path\n");
153 return NTS_ERR_FAILED;
156 rcl = lyd_new_path(netconf_node, 0, IETF_NETCONF_SERVER_CH_SSH_TCP_CLIENT_SCHEMA_XPATH"/keepalives/max-probes", "10", 0, LYD_PATH_OPT_NOPARENTRET);
158 log_error("could not created yang path\n");
160 return NTS_ERR_FAILED;
163 rcl = lyd_new_path(netconf_node, 0, IETF_NETCONF_SERVER_CH_SSH_TCP_CLIENT_SCHEMA_XPATH"/keepalives/probe-interval", "5", 0, LYD_PATH_OPT_NOPARENTRET);
165 log_error("could not created yang path\n");
167 return NTS_ERR_FAILED;
170 rcl = lyd_new_path(netconf_node, 0, IETF_NETCONF_SERVER_CH_SSH_TCP_CLIENT_SCHEMA_XPATH"/remote-address", controller_ip, 0, LYD_PATH_OPT_NOPARENTRET);
172 log_error("could not created yang path\n");
174 return NTS_ERR_FAILED;
179 sprintf(port, "%d", controller_callhome_port);
180 rcl = lyd_new_path(netconf_node, 0, IETF_NETCONF_SERVER_CH_SSH_TCP_CLIENT_SCHEMA_XPATH"/remote-port", port, 0, LYD_PATH_OPT_NOPARENTRET);
182 log_error("could not created yang path\n");
183 return NTS_ERR_FAILED;
186 rcl = lyd_new_path(netconf_node, 0, IETF_NETCONF_SERVER_CH_SSH_SERVER_PARAMS_SCEHMA_XPATH"/server-identity/host-key[name='default-key']/public-key/keystore-reference", KS_KEY_NAME, 0, LYD_PATH_OPT_NOPARENTRET);
188 log_error("could not created yang path\n");
189 return NTS_ERR_FAILED;
192 rcl = lyd_new_path(netconf_node, 0, IETF_NETCONF_SERVER_CH_SSH_SERVER_PARAMS_SCEHMA_XPATH"/client-authentication/supported-authentication-methods/publickey", "", 0, LYD_PATH_OPT_NOPARENTRET);
194 log_error("could not created yang path\n");
195 return NTS_ERR_FAILED;
198 rcl = lyd_new_path(netconf_node, 0, IETF_NETCONF_SERVER_CH_SSH_SERVER_PARAMS_SCEHMA_XPATH"/client-authentication/supported-authentication-methods/passsword", "", 0, LYD_PATH_OPT_NOPARENTRET);
200 log_error("could not created yang path\n");
201 return NTS_ERR_FAILED;
204 rcl = lyd_new_path(netconf_node, 0, IETF_NETCONF_SERVER_CH_SSH_SERVER_PARAMS_SCEHMA_XPATH"/client-authentication/supported-authentication-methods/other", "interactive", 0, LYD_PATH_OPT_NOPARENTRET);
206 log_error("could not created yang path\n");
207 return NTS_ERR_FAILED;
210 rcl = lyd_new_path(netconf_node, 0, IETF_NETCONF_SERVER_CH_SSH_SERVER_PARAMS_SCEHMA_XPATH"/client-authentication/users", "", 0, LYD_PATH_OPT_NOPARENTRET);
212 log_error("could not created yang path\n");
213 return NTS_ERR_FAILED;
215 rcl = lyd_new_path(netconf_node, 0, IETF_NETCONF_SERVER_CH_CONN_PERSISTENT_SCHEMA_XPATH, "", 0, LYD_PATH_OPT_NOPARENTRET);
217 log_error("could not created yang path\n");
218 return NTS_ERR_FAILED;
221 int rc = send_odl_callhome_configuration(current_session, false);
222 if(rc != NTS_ERR_OK) {
223 log_add_verbose(2, "could not send ODL Call Home configuration.\n");
229 static int create_tls_callhome_endpoint(sr_session_ctx_t *current_session, struct lyd_node *netconf_node) {
230 assert(current_session);
231 assert(netconf_node);
233 controller_details_t *controller = controller_details_get(current_session);
234 if(controller == 0) {
235 log_error("controller_details_get failed\n");
236 return NTS_ERR_FAILED;
239 char *controller_ip = strdup(controller->nc_callhome_ip);
240 uint16_t controller_callhome_port = controller->nc_callhome_port;
241 controller_details_free(controller);
243 if(controller_ip == 0) {
244 log_error("strdup failed\n");
245 return NTS_ERR_FAILED;
248 struct lyd_node *rcl = lyd_new_path(netconf_node, 0, IETF_NETCONF_SERVER_CH_TLS_TCP_CLIENT_SCHEMA_XPATH"/keepalives/idle-time", "1", 0, LYD_PATH_OPT_NOPARENTRET);
250 log_error("could not created yang path\n");
252 return NTS_ERR_FAILED;
255 rcl = lyd_new_path(netconf_node, 0, IETF_NETCONF_SERVER_CH_TLS_TCP_CLIENT_SCHEMA_XPATH"/keepalives/max-probes", "10", 0, LYD_PATH_OPT_NOPARENTRET);
257 log_error("could not created yang path\n");
259 return NTS_ERR_FAILED;
262 rcl = lyd_new_path(netconf_node, 0, IETF_NETCONF_SERVER_CH_TLS_TCP_CLIENT_SCHEMA_XPATH"/keepalives/probe-interval", "5", 0, LYD_PATH_OPT_NOPARENTRET);
264 log_error("could not created yang path\n");
266 return NTS_ERR_FAILED;
269 rcl = lyd_new_path(netconf_node, 0, IETF_NETCONF_SERVER_CH_TLS_TCP_CLIENT_SCHEMA_XPATH"/remote-address", controller_ip, 0, LYD_PATH_OPT_NOPARENTRET);
271 log_error("could not created yang path\n");
273 return NTS_ERR_FAILED;
278 sprintf(port, "%d", controller_callhome_port);
279 rcl = lyd_new_path(netconf_node, 0, IETF_NETCONF_SERVER_CH_TLS_TCP_CLIENT_SCHEMA_XPATH"/remote-port", port, 0, LYD_PATH_OPT_NOPARENTRET);
281 log_error("could not created yang path\n");
282 return NTS_ERR_FAILED;
285 rcl = lyd_new_path(netconf_node, 0, IETF_NETCONF_SERVER_CH_TLS_SERVER_PARAMS_SCEHMA_XPATH"/server-identity/keystore-reference/asymmetric-key", KS_KEY_NAME, 0, LYD_PATH_OPT_NOPARENTRET);
287 log_error("could not created yang path\n");
288 return NTS_ERR_FAILED;
291 rcl = lyd_new_path(netconf_node, 0, IETF_NETCONF_SERVER_CH_TLS_SERVER_PARAMS_SCEHMA_XPATH"/server-identity/keystore-reference/certificate", KS_CERT_NAME, 0, LYD_PATH_OPT_NOPARENTRET);
293 log_error("could not created yang path\n");
294 return NTS_ERR_FAILED;
297 rcl = lyd_new_path(netconf_node, 0, IETF_NETCONF_SERVER_CH_TLS_SERVER_PARAMS_SCEHMA_XPATH"/client-authentication/required", "", 0, LYD_PATH_OPT_NOPARENTRET);
299 log_error("could not created yang path\n");
300 return NTS_ERR_FAILED;
303 rcl = lyd_new_path(netconf_node, 0, IETF_NETCONF_SERVER_CH_TLS_SERVER_PARAMS_SCEHMA_XPATH"/client-authentication/ca-certs", "cacerts", 0, LYD_PATH_OPT_NOPARENTRET);
305 log_error("could not created yang path\n");
306 return NTS_ERR_FAILED;
309 rcl = lyd_new_path(netconf_node, 0, IETF_NETCONF_SERVER_CH_TLS_SERVER_PARAMS_SCEHMA_XPATH"/client-authentication/client-certs", "clientcerts", 0, LYD_PATH_OPT_NOPARENTRET);
311 log_error("could not created yang path\n");
312 return NTS_ERR_FAILED;
315 rcl = lyd_new_path(netconf_node, 0, IETF_NETCONF_SERVER_CH_TLS_SERVER_PARAMS_SCEHMA_XPATH"/client-authentication/cert-maps/cert-to-name[id='1']/fingerprint", "02:E9:38:1F:F6:8B:62:DE:0A:0B:C5:03:81:A8:03:49:A0:00:7F:8B:F3", 0, LYD_PATH_OPT_NOPARENTRET);
317 log_error("could not created yang path\n");
318 return NTS_ERR_FAILED;
321 rcl = lyd_new_path(netconf_node, session_context, IETF_NETCONF_SERVER_CH_TLS_SERVER_PARAMS_SCEHMA_XPATH"/client-authentication/cert-maps/cert-to-name[id='1']/map-type", "ietf-x509-cert-to-name:specified", 0, LYD_PATH_OPT_NOPARENTRET);
323 log_error("could not created yang path\n");
324 return NTS_ERR_FAILED;
327 rcl = lyd_new_path(netconf_node, 0, IETF_NETCONF_SERVER_CH_TLS_SERVER_PARAMS_SCEHMA_XPATH"/client-authentication/cert-maps/cert-to-name[id='1']/name", "netconf", 0, LYD_PATH_OPT_NOPARENTRET);
329 log_error("could not created yang path\n");
330 return NTS_ERR_FAILED;
333 rcl = lyd_new_path(netconf_node, 0, IETF_NETCONF_SERVER_CH_CONN_PERSISTENT_SCHEMA_XPATH, "", 0, LYD_PATH_OPT_NOPARENTRET);
335 log_error("could not created yang path\n");
336 return NTS_ERR_FAILED;
339 int rc = send_odl_callhome_configuration(current_session, true);
340 if(rc != NTS_ERR_OK) {
341 log_add_verbose(2, "could not send ODL Call Home configuration.\n");
348 static int send_odl_add_trusted_certificate(sr_session_ctx_t *current_session) {
349 assert(current_session);
351 char *server_cert = read_key(SERVER_CERT_PATH);
352 if(server_cert == 0) {
353 log_error("could not read the serevr certificate from file %s\n", SERVER_CERT_PATH);
354 return NTS_ERR_FAILED;
357 char *odl_trusted_certificate_payload = 0;
358 asprintf(&odl_trusted_certificate_payload, NETCONF_TRUSTED_CERTIFICATE_CURL_SEND_PAYLOAD_FORMAT, framework_environment.settings.hostname, server_cert);
359 if(odl_trusted_certificate_payload == 0) {
360 log_error("bad asprintf\n");
361 return NTS_ERR_FAILED;
365 controller_details_t *controller = controller_details_get(current_session);
366 if(controller == 0) {
367 log_error("controller_details_get failed\n");
368 return NTS_ERR_FAILED;
372 asprintf(&url, "%s/rests/operations/netconf-keystore:add-trusted-certificate", controller->base_url);
374 log_error("bad asprintf\n");
375 controller_details_free(controller);
376 return NTS_ERR_FAILED;
379 int rc = http_request(url, controller->username, controller->password, "POST", odl_trusted_certificate_payload, 0, 0);
380 if(rc != NTS_ERR_OK) {
381 log_error("http_request failed\n");
385 controller_details_free(controller);
386 free(odl_trusted_certificate_payload);
391 static int send_odl_callhome_configuration(sr_session_ctx_t *current_session, bool is_tls) {
392 assert(current_session);
394 char *odl_callhome_payload = 0;
396 char *public_ssh_key = read_key(SERVER_PUBLIC_SSH_KEY_PATH);
397 if(public_ssh_key == 0) {
398 log_error("could not read the public ssh key from file %s\n", SERVER_PUBLIC_SSH_KEY_PATH);
399 return NTS_ERR_FAILED;
402 char *ssh_key_string;
403 ssh_key_string = strtok(public_ssh_key, " ");
404 ssh_key_string = strtok(NULL, " ");
405 ssh_key_string[strlen(ssh_key_string) - 1] = 0; // trim the newline character
407 // checkAS we have hardcoded here the username and password of the NETCONF Server
408 asprintf(&odl_callhome_payload, NETCONF_SSH_CALLHOME_CURL_SEND_PAYLOAD_FORMAT, framework_environment.settings.hostname, ssh_key_string);
409 free(public_ssh_key);
412 int ret = send_odl_add_trusted_certificate(current_session);
413 if (ret != NTS_ERR_OK) {
414 log_error("Could not send trusted certificate to ODL.");
415 return NTS_ERR_FAILED;
417 // checkAS we have hardcoded here the private key of ODL
418 asprintf(&odl_callhome_payload, NETCONF_TLS_CALLHOME_CURL_SEND_PAYLOAD_FORMAT, framework_environment.settings.hostname, framework_environment.settings.hostname, "ODL_private_key_0");
421 if(odl_callhome_payload == 0) {
422 log_error("bad asprintf\n");
423 return NTS_ERR_FAILED;
426 controller_details_t *controller = controller_details_get(current_session);
427 if(controller == 0) {
428 log_error("controller_details_get failed\n");
429 return NTS_ERR_FAILED;
433 asprintf(&url, "%s/rests/data/odl-netconf-callhome-server:netconf-callhome-server/allowed-devices/device=%s", controller->base_url, framework_environment.settings.hostname);
435 log_error("bad asprintf\n");
436 controller_details_free(controller);
437 return NTS_ERR_FAILED;
440 int rc = http_request(url, controller->username, controller->password, "PUT", odl_callhome_payload, 0, 0);
441 if(rc != NTS_ERR_OK) {
442 log_error("http_request failed\n");
446 controller_details_free(controller);
447 free(odl_callhome_payload);