59b2e58b10ed2cb651545156c081fab7c5441aba
[sim/o1-interface.git] / ntsimulator / ntsim-ng / features / netconf_call_home / netconf_call_home.c
1 /*************************************************************************
2 *
3 * Copyright 2020 highstreet technologies GmbH and others
4 *
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
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
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 ***************************************************************************/
17
18 #define _GNU_SOURCE
19
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"
26 #include <stdio.h>
27 #include <assert.h>
28
29 #include "core/session.h"
30 #include "core/framework.h"
31 #include "core/xpath.h"
32
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\"}]}}"
36
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);
41
42 static int netconf_call_home_status = 0;
43
44 int netconf_call_home_feature_get_status(void) {
45     return netconf_call_home_status;
46 }
47
48 int netconf_call_home_feature_start(sr_session_ctx_t *current_session) {
49     assert(current_session);
50     assert_session();
51
52     sr_val_t *value = 0;
53
54     bool callhome_enabled = false;
55     int rc = sr_get_item(current_session, NTS_NF_NETCONF_CALLHOME_ENABLED_SCHEMA_PATH, 0, &value);
56     if(rc == SR_ERR_OK) {
57         callhome_enabled = value->data.bool_val;
58         sr_free_val(value);
59     }
60     else {
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;
64         }
65     }
66
67     if(callhome_enabled == false) {
68         log_add_verbose(2, "NETCONF CallHome is not enabled, not configuring NETCONF Server.\n");
69         return NTS_ERR_OK;
70     }
71
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;
77     }
78
79     controller_details_t *controller = controller_details_get(current_session);
80     if(controller == 0) {
81         log_error("controller_details_get failed\n");
82         return NTS_ERR_FAILED;
83     }
84
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;
92         }
93     }
94     else {
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;
101         }
102     }
103     controller_details_free(controller);
104
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;
109     }
110
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;
115     }
116
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;
121     }
122
123     lyd_free_withsiblings(netconf_node);
124     netconf_call_home_status = 1;
125
126     return NTS_ERR_OK;
127 }
128
129
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);
133
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;
138     }
139
140     char *controller_ip = strdup(controller->ip);
141     uint16_t controller_callhome_port = controller->nc_callhome_port;
142     controller_details_free(controller);
143
144     if(controller_ip == 0) {
145         log_error("strdup failed\n");
146         return NTS_ERR_FAILED;
147     }
148
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);
150     if(rcl == 0) {
151         log_error("could not created yang path\n");
152         free(controller_ip);
153         return NTS_ERR_FAILED;
154     }
155
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);
157     if(rcl == 0) {
158         log_error("could not created yang path\n");
159         free(controller_ip);
160         return NTS_ERR_FAILED;
161     }
162
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);
164     if(rcl == 0) {
165         log_error("could not created yang path\n");
166         free(controller_ip);
167         return NTS_ERR_FAILED;
168     }
169
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);
171     if(rcl == 0) {
172         log_error("could not created yang path\n");
173         free(controller_ip);
174         return NTS_ERR_FAILED;
175     }
176     free(controller_ip);
177
178     char port[20];
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);
181     if(rcl == 0) {
182         log_error("could not created yang path\n");
183         return NTS_ERR_FAILED;
184     }
185
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);
187     if(rcl == 0) {
188         log_error("could not created yang path\n");
189         return NTS_ERR_FAILED;
190     }
191
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);
193     if(rcl == 0) {
194         log_error("could not created yang path\n");
195         return NTS_ERR_FAILED;
196     }
197
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);
199     if(rcl == 0) {
200         log_error("could not created yang path\n");
201         return NTS_ERR_FAILED;
202     }
203
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);
205     if(rcl == 0) {
206         log_error("could not created yang path\n");
207         return NTS_ERR_FAILED;
208     }
209
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);
211     if(rcl == 0) {
212         log_error("could not created yang path\n");
213         return NTS_ERR_FAILED;
214     }
215     rcl = lyd_new_path(netconf_node, 0, IETF_NETCONF_SERVER_CH_CONN_PERSISTENT_SCHEMA_XPATH, "", 0, LYD_PATH_OPT_NOPARENTRET);
216     if(rcl == 0) {
217         log_error("could not created yang path\n");
218         return NTS_ERR_FAILED;
219     }
220
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");
224     }
225
226     return NTS_ERR_OK;
227 }
228
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);
232
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;
237     }
238
239     char *controller_ip = strdup(controller->ip);
240     uint16_t controller_callhome_port = controller->nc_callhome_port;
241     controller_details_free(controller);
242
243     if(controller_ip == 0) {
244         log_error("strdup failed\n");
245         return NTS_ERR_FAILED;
246     }
247     
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);
249     if(rcl == 0) {
250         log_error("could not created yang path\n");
251         free(controller_ip);
252         return NTS_ERR_FAILED;
253     }
254
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);
256     if(rcl == 0) {
257         log_error("could not created yang path\n");
258         free(controller_ip);
259         return NTS_ERR_FAILED;
260     }
261
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);
263     if(rcl == 0) {
264         log_error("could not created yang path\n");
265         free(controller_ip);
266         return NTS_ERR_FAILED;
267     }
268
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);
270     if(rcl == 0) {
271         log_error("could not created yang path\n");
272         free(controller_ip);
273         return NTS_ERR_FAILED;
274     }
275     free(controller_ip);
276
277     char port[20];
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);
280     if(rcl == 0) {
281         log_error("could not created yang path\n");
282         return NTS_ERR_FAILED;
283     }
284
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);
286     if(rcl == 0) {
287         log_error("could not created yang path\n");
288         return NTS_ERR_FAILED;
289     }
290
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);
292     if(rcl == 0) {
293         log_error("could not created yang path\n");
294         return NTS_ERR_FAILED;
295     }
296
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);
298     if(rcl == 0) {
299         log_error("could not created yang path\n");
300         return NTS_ERR_FAILED;
301     }
302
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);
304     if(rcl == 0) {
305         log_error("could not created yang path\n");
306         return NTS_ERR_FAILED;
307     }
308
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);
310     if(rcl == 0) {
311         log_error("could not created yang path\n");
312         return NTS_ERR_FAILED;
313     }
314
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);
316     if(rcl == 0) {
317         log_error("could not created yang path\n");
318         return NTS_ERR_FAILED;
319     }
320
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);
322     if(rcl == 0) {
323         log_error("could not created yang path\n");
324         return NTS_ERR_FAILED;
325     }
326
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);
328     if(rcl == 0) {
329         log_error("could not created yang path\n");
330         return NTS_ERR_FAILED;
331     }
332
333     rcl = lyd_new_path(netconf_node, 0, IETF_NETCONF_SERVER_CH_CONN_PERSISTENT_SCHEMA_XPATH, "", 0, LYD_PATH_OPT_NOPARENTRET);
334     if(rcl == 0) {
335         log_error("could not created yang path\n");
336         return NTS_ERR_FAILED;
337     }
338
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");
342     }
343
344     return NTS_ERR_OK;
345 }
346
347
348 static int send_odl_add_trusted_certificate(sr_session_ctx_t *current_session) {
349     assert(current_session);
350
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;
355     }
356
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;
362     }
363     free(server_cert);
364
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;
369     }
370     
371     char *url = 0;
372     asprintf(&url, "%s/rests/operations/netconf-keystore:add-trusted-certificate", controller->base_url);
373     if(url == 0) {
374         log_error("bad asprintf\n");
375         controller_details_free(controller);
376         return NTS_ERR_FAILED;
377     }
378
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");
382     }
383     
384     free(url);
385     controller_details_free(controller);
386     free(odl_trusted_certificate_payload);
387
388     return rc;
389 }
390
391 static int send_odl_callhome_configuration(sr_session_ctx_t *current_session, bool is_tls) {
392     assert(current_session);
393
394     char *odl_callhome_payload = 0;
395     if (!is_tls) {
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;
400         }
401
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
406
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);
410     }
411     else {
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;
416         }
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");
419     }
420
421     if(odl_callhome_payload == 0) {
422         log_error("bad asprintf\n");
423         return NTS_ERR_FAILED;
424     }
425
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;
430     }
431     
432     char *url = 0;
433     asprintf(&url, "%s/rests/data/odl-netconf-callhome-server:netconf-callhome-server/allowed-devices/device=%s", controller->base_url, framework_environment.settings.hostname);
434     if(url == 0) {
435         log_error("bad asprintf\n");
436         controller_details_free(controller);
437         return NTS_ERR_FAILED;
438     }
439
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");
443     }
444     
445     free(url);
446     controller_details_free(controller);
447     free(odl_callhome_payload);
448
449     return rc;
450 }