1 #include <nm_core.h>
2
3 #if defined (NM_WITH_REMOTE)
4 #include <nm_qmp_control.h>
5 #include <nm_remote_api.h>
6 #include <nm_mon_daemon.h>
7 #include <nm_vm_control.h>
8 #include <nm_database.h>
9 #include <nm_utils.h>
10
11 #include <sys/socket.h>
12 #include <netinet/in.h>
13 #include <arpa/inet.h>
14 #include <pthread.h>
15 #include <poll.h>
16
17 #include <openssl/ssl.h>
18 #include <openssl/err.h>
19
20 #define NM_API_POLL_MAXFDS 256
21
22 static nm_api_ctx_t *mon_data;
23
24 static int nm_api_socket(int *sock);
25 static SSL_CTX *nm_api_tls_setup(void);
26 static void nm_api_serve(SSL *tls);
27 static int nm_api_check_auth(struct json_object *request, nm_str_t *reply);
28 static void nm_api_reply(const char *request, nm_str_t *reply);
29
30 /* API methods */
31 static void nm_api_md_version(struct json_object *request, nm_str_t *reply);
32 static void nm_api_md_nemu_version(struct json_object *request, nm_str_t *reply);
33 static void nm_api_md_auth(struct json_object *request, nm_str_t *reply);
34 static void nm_api_md_vmlist(struct json_object *request, nm_str_t *reply);
35 static void nm_api_md_vmstart(struct json_object *request, nm_str_t *reply);
36 static void nm_api_md_vmstop(struct json_object *request, nm_str_t *reply);
37 static void nm_api_md_vmforcestop(struct json_object *request, nm_str_t *reply);
38 static void nm_api_md_vmgetconnectport(struct json_object *request, nm_str_t *reply);
39
40 static nm_api_ops_t nm_api[] = {
41 { .method = "nemu_version", .run = nm_api_md_nemu_version },
42 { .method = "api_version", .run = nm_api_md_version },
43 { .method = "auth", .run = nm_api_md_auth },
44 { .method = "vm_list", .run = nm_api_md_vmlist },
45 { .method = "vm_start", .run = nm_api_md_vmstart },
46 { .method = "vm_stop", .run = nm_api_md_vmstop },
47 { .method = "vm_force_stop", .run = nm_api_md_vmforcestop },
48 { .method = "vm_get_connect_port", .run = nm_api_md_vmgetconnectport }
49 };
50
nm_api_server(void * ctx)51 void *nm_api_server(void *ctx)
52 {
53 struct pollfd fds[NM_API_POLL_MAXFDS];
54 int sd, timeout, nfds = 1;
55 SSL_CTX *tls_ctx = NULL;
56 mon_data = ctx;
57
58 SSL_library_init();
59 if ((tls_ctx = nm_api_tls_setup()) == NULL) {
60 pthread_exit(NULL);
61 }
62
63 if (nm_api_socket(&sd) != NM_OK) {
64 pthread_exit(NULL);
65 }
66
67 memset(fds, 0, sizeof(fds));
68 fds[0].fd = sd;
69 fds[0].events = POLLIN;
70 timeout = 1000; /* 1 second */
71
72 nm_db_init();
73
74 while (!mon_data->ctrl->stop) {
75 struct sockaddr_in cl_addr;
76 socklen_t len = sizeof(cl_addr);
77 bool rebuild_fds = false;
78 int cl_sd, rc, cur_fds;
79
80 rc = poll(fds, nfds, timeout);
81 if (rc == -1) { /*error */
82 nm_debug("%s: poll() error: %s\n", __func__, strerror(errno));
83 goto out;
84 } else if (rc == 0) { /* nothing happens */
85 continue;
86 }
87
88 cur_fds = nfds;
89 for (int n = 0; n < cur_fds; n++) {
90 if (fds[n].revents == 0) {
91 continue;
92 }
93
94 /* check for unexpected result */
95 if (fds[n].revents != POLLIN) {
96 nm_debug("%s: unexpected event: %d\n", __func__, fds[n].revents);
97 goto out;
98 }
99
100 if (fds[n].fd == sd) { /* server socket event */
101 do { /* accept all incoming connections */
102 cl_sd = accept(sd, (struct sockaddr *) &cl_addr, &len);
103 if (cl_sd < 0) {
104 if (errno != EWOULDBLOCK) {
105 nm_debug("%s: accept error: %s\n",
106 __func__, strerror(errno));
107 goto out;
108 }
109 break;
110 }
111 if (fcntl(cl_sd, F_SETFD, FD_CLOEXEC) == -1) {
112 nm_debug("%s: fcntl error: %s\n",
113 __func__, strerror(errno));
114 goto out;
115 }
116
117 nm_debug("%s: connect to API from: %s\n",
118 __func__, inet_ntoa(cl_addr.sin_addr));
119 fds[nfds].fd = cl_sd;
120 fds[nfds].events = POLLIN;
121 if (nfds + 1 == NM_API_POLL_MAXFDS) {
122 nm_debug("%s: max clients <%d> reached\n",
123 __func__, NM_API_POLL_MAXFDS);
124 continue;
125 }
126 nfds++;
127 } while (cl_sd != -1);
128 } else { /* client socket event */
129 SSL *tls;
130
131 if ((tls = SSL_new(tls_ctx)) == NULL) {
132 nm_debug("%s: %s\n", __func__,
133 ERR_error_string(ERR_get_error(), NULL));
134 close(fds[n].fd);
135 goto out;
136 }
137 SSL_set_fd(tls, fds[n].fd);
138 nm_api_serve(tls);
139 SSL_free(tls);
140 fds[n].fd = -1;
141 rebuild_fds = true;
142 }
143 }
144
145 if (rebuild_fds) {
146 for (int i = 0; i < nfds; i++) {
147 if (fds[i].fd == -1) {
148 for (int j = i; j < nfds; j++) {
149 fds[j].fd = fds[j + 1].fd;
150 }
151 i--;
152 nfds--;
153 }
154 }
155 rebuild_fds = false;
156 }
157 }
158 out:
159 close(sd);
160 for (int i = 0; i < nfds; i++) {
161 if (fds[i].fd > 0) {
162 close(fds[i].fd);
163 }
164 }
165
166 SSL_CTX_free(tls_ctx);
167 nm_db_close();
168
169 pthread_exit(NULL);
170 }
171
nm_api_reply(const char * request,nm_str_t * reply)172 static void nm_api_reply(const char *request, nm_str_t *reply)
173 {
174 struct json_object *parsed, *exec;
175 const char *method;
176
177 parsed = json_tokener_parse(request);
178 if (!parsed) {
179 nm_str_format(reply, NM_API_RET_ERR, "cannot parse json");
180 return;
181 }
182
183 json_object_object_get_ex(parsed, "exec", &exec);
184 if (!exec) {
185 nm_str_format(reply, NM_API_RET_ERR, "exec param is missing");
186 return;
187 }
188
189 method = json_object_get_string(exec);
190
191 for (size_t n = 0; n < nm_arr_len(nm_api); n++) {
192 if (nm_str_cmp_tt(nm_api[n].method, method) == NM_OK) {
193 nm_api[n].run(parsed, reply);
194 return;
195 }
196 }
197
198 nm_str_format(reply, NM_API_RET_ERR, "unknown method");
199 json_object_put(parsed);
200 }
201
nm_api_serve(SSL * tls)202 static void nm_api_serve(SSL *tls)
203 {
204 char buf[NM_API_CL_BUF_LEN] = {0};
205 int sd, nread;
206
207 if (SSL_accept(tls) != 1) {
208 nm_debug("%s: %s\n", __func__, ERR_error_string(ERR_get_error(), NULL));
209 goto out;
210 }
211
212 nread = SSL_read(tls, buf, sizeof(buf));
213 if (nread > 0) {
214 nm_str_t reply = NM_INIT_STR;
215 buf[nread] = '\0';
216 if (buf[nread - 1] == '\n') {
217 buf[nread - 1] = '\0';
218 }
219 nm_debug("%s: got API cmd: \"%s\"\n", __func__, buf);
220 nm_api_reply(buf, &reply);
221
222 if (reply.len) {
223 int rc;
224 rc = SSL_write(tls, reply.data, reply.len);
225 if (rc <= 0) {
226 nm_debug("%s: %s\n", __func__,
227 ERR_error_string(ERR_get_error(), NULL));
228 }
229 }
230 nm_str_free(&reply);
231 }
232
233 out:
234 sd = SSL_get_fd(tls);
235 SSL_shutdown(tls);
236 close(sd);
237 }
238
nm_api_tls_setup(void)239 static SSL_CTX *nm_api_tls_setup(void)
240 {
241 const nm_cfg_t *cfg = nm_cfg_get();
242 SSL_CTX *ctx = NULL;
243
244 OpenSSL_add_all_algorithms();
245 SSL_load_error_strings();
246
247 if ((ctx = SSL_CTX_new(TLS_server_method())) == NULL) {
248 nm_debug("%s: %s\n", __func__, ERR_error_string(ERR_get_error(), NULL));
249 return NULL;
250 }
251
252 if (SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION) != 1) {
253 nm_debug("%s: %s\n", __func__, ERR_error_string(ERR_get_error(), NULL));
254 goto err;
255 }
256
257 if (SSL_CTX_use_certificate_file(ctx, cfg->api_cert_path.data,
258 SSL_FILETYPE_PEM) != 1) {
259 nm_debug("%s: %s\n", __func__, ERR_error_string(ERR_get_error(), NULL));
260 goto err;
261 }
262
263 if (SSL_CTX_use_PrivateKey_file(ctx, cfg->api_key_path.data,
264 SSL_FILETYPE_PEM) != 1) {
265 nm_debug("%s: %s\n", __func__, ERR_error_string(ERR_get_error(), NULL));
266 goto err;
267 }
268
269 if (SSL_CTX_check_private_key(ctx) != 1) {
270 nm_debug("%s:%s\n", __func__, ERR_error_string(ERR_get_error(), NULL));
271 goto err;
272 }
273
274 return ctx;
275 err:
276 SSL_CTX_free(ctx);
277 return NULL;
278 }
279
nm_api_socket(int * sock)280 static int nm_api_socket(int *sock)
281 {
282 struct sockaddr_in addr;
283 int sd, opt = 1;
284
285 if ((sd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
286 nm_debug("%s: error create socket: %s\n", __func__, strerror(errno));
287 return NM_ERR;
288 }
289
290 if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int)) == -1) {
291 nm_debug("%s: error set socket opts: %s\n", __func__, strerror(errno));
292 close(sd);
293 return NM_ERR;
294 }
295
296 if (fcntl(sd, F_SETFL, O_NONBLOCK) == -1) {
297 nm_debug("%s: set O_NONBLOCK failed: %s\n", __func__, strerror(errno));
298 close(sd);
299 return NM_ERR;
300 }
301
302 memset(&addr, 0, sizeof(addr));
303 addr.sin_family = AF_INET;
304 addr.sin_port = htons(nm_cfg_get()->api_port);
305 addr.sin_addr.s_addr = INADDR_ANY;
306
307 if (bind(sd, (struct sockaddr *) &addr, sizeof(addr)) == -1) {
308 nm_debug("%s: cannot bind: %s\n", __func__, strerror(errno));
309 return NM_ERR;
310 }
311
312 if (listen(sd, SOMAXCONN) == -1) {
313 nm_debug("%s: cannot listen: %s\n", __func__, strerror(errno));
314 return NM_ERR;
315 }
316
317 *sock = sd;
318
319 return NM_OK;
320 }
321
nm_api_check_auth(struct json_object * request,nm_str_t * reply)322 static int nm_api_check_auth(struct json_object *request, nm_str_t *reply)
323 {
324 unsigned char hash[SHA256_DIGEST_LENGTH];
325 char hash_str[NM_API_SHA256_LEN];
326 struct json_object *auth;
327 const char *pass;
328 nm_str_t salted_pass = NM_INIT_STR;
329 SHA256_CTX sha256;
330
331 json_object_object_get_ex(request, "auth", &auth);
332 if (!auth) {
333 nm_str_format(reply, NM_API_RET_ERR, "auth param is missing");
334 goto out;
335 }
336
337 pass = json_object_get_string(auth);
338 nm_str_format(&salted_pass, "%s%s", pass, nm_cfg_get()->api_salt.data);
339
340 SHA256_Init(&sha256);
341 SHA256_Update(&sha256, salted_pass.data, salted_pass.len);
342 SHA256_Final(hash, &sha256);
343
344 for (int n = 0; n < SHA256_DIGEST_LENGTH; n++) {
345 sprintf(hash_str + (n * 2), "%02x", hash[n]);
346 }
347 hash_str[NM_API_SHA256_LEN - 1] = '\0';
348
349 if (nm_str_cmp_st(&nm_cfg_get()->api_hash, hash_str) == NM_OK) {
350 nm_str_free(&salted_pass);
351 return NM_OK;
352 }
353
354 nm_str_format(reply, NM_API_RET_ERR, "access denied");
355 out:
356 nm_str_free(&salted_pass);
357 return NM_ERR;
358 }
359
nm_api_md_version(struct json_object * request,nm_str_t * reply)360 static void nm_api_md_version(struct json_object *request, nm_str_t *reply)
361 {
362 nm_str_format(reply, NM_API_RET_VAL, NM_API_VERSION);
363 json_object_put(request);
364 }
365
nm_api_md_nemu_version(struct json_object * request,nm_str_t * reply)366 static void nm_api_md_nemu_version(struct json_object *request, nm_str_t *reply)
367 {
368 int rc = nm_api_check_auth(request, reply);
369
370 if (rc != NM_OK) {
371 goto out;
372 }
373
374 nm_str_format(reply, NM_API_RET_VAL, NM_VERSION);
375 out:
376 json_object_put(request);
377 }
378
nm_api_md_auth(struct json_object * request,nm_str_t * reply)379 static void nm_api_md_auth(struct json_object *request, nm_str_t *reply)
380 {
381 int rc = nm_api_check_auth(request, reply);
382
383 if (rc == NM_OK) {
384 nm_str_format(reply, "%s", NM_API_RET_OK);
385 }
386
387 json_object_put(request);
388 }
389
nm_api_md_vmlist(struct json_object * request,nm_str_t * reply)390 static void nm_api_md_vmlist(struct json_object *request, nm_str_t *reply)
391 {
392 int rc = nm_api_check_auth(request, reply);
393 nm_mon_vms_t *vms = mon_data->vms;
394 nm_str_t list = NM_INIT_STR;
395
396 if (rc != NM_OK) {
397 goto out;
398 }
399
400 pthread_mutex_lock(&vms->mtx);
401 for (size_t n = 0; n < vms->list->n_memb; n++) {
402 nm_str_append_format(&list, "%s{\"name\":\"%s\",\"status\":%s}",
403 (n) ? "," : "",
404 nm_mon_item_get_name_cstr(vms->list, n),
405 (nm_mon_item_get_status(vms->list, n) == NM_TRUE) ?
406 "true" : "false");
407 }
408 pthread_mutex_unlock(&vms->mtx);
409 nm_str_format(reply, NM_API_RET_ARRAY, list.data);
410
411 out:
412 nm_str_free(&list);
413 json_object_put(request);
414 }
415
nm_api_md_vmstart(struct json_object * request,nm_str_t * reply)416 static void nm_api_md_vmstart(struct json_object *request, nm_str_t *reply)
417 {
418 int rc = nm_api_check_auth(request, reply);
419 nm_mon_vms_t *vms = mon_data->vms;
420 nm_str_t vmname = NM_INIT_STR;
421 struct json_object *name;
422 bool vm_exist = false;
423 const char *name_str;
424
425 if (rc != NM_OK) {
426 goto out;
427 }
428
429 json_object_object_get_ex(request, "name", &name);
430 if (!name) {
431 nm_str_format(reply, NM_API_RET_ERR, "name param is missing");
432 goto out;
433 }
434
435 name_str = json_object_get_string(name);
436 for (size_t n = 0; n < vms->list->n_memb; n++) {
437 if (nm_str_cmp_st(nm_mon_item_get_name(vms->list, n),
438 name_str) == NM_OK) {
439 vm_exist = true;
440 break;
441 }
442 }
443
444 if (!vm_exist) {
445 nm_str_format(reply, NM_API_RET_ERR, "VM does not exists");
446 goto out;
447 }
448
449 nm_str_format(&vmname, "%s", name_str);
450 if (nm_qmp_test_socket(&vmname) != NM_OK) {
451 nm_vmctl_start(&vmname, 0);
452 nm_str_format(reply, "%s", NM_API_RET_OK);
453 } else {
454 nm_str_format(reply, NM_API_RET_ERR, "already started");
455 }
456 out:
457 nm_str_free(&vmname);
458 json_object_put(request);
459 }
460
nm_api_md_vmstop(struct json_object * request,nm_str_t * reply)461 static void nm_api_md_vmstop(struct json_object *request, nm_str_t *reply)
462 {
463 int rc = nm_api_check_auth(request, reply);
464 nm_mon_vms_t *vms = mon_data->vms;
465 nm_str_t vmname = NM_INIT_STR;
466 struct json_object *name;
467 bool vm_exist = false;
468 const char *name_str;
469
470 if (rc != NM_OK) {
471 goto out;
472 }
473
474 json_object_object_get_ex(request, "name", &name);
475 if (!name) {
476 nm_str_format(reply, NM_API_RET_ERR, "name param is missing");
477 goto out;
478 }
479
480 name_str = json_object_get_string(name);
481 for (size_t n = 0; n < vms->list->n_memb; n++) {
482 if (nm_str_cmp_st(nm_mon_item_get_name(vms->list, n),
483 name_str) == NM_OK) {
484 vm_exist = true;
485 break;
486 }
487 }
488
489 if (!vm_exist) {
490 nm_str_format(reply, NM_API_RET_ERR, "VM does not exists");
491 goto out;
492 }
493
494 nm_str_format(&vmname, "%s", name_str);
495 nm_qmp_vm_shut(&vmname);
496 nm_str_format(reply, "%s", NM_API_RET_OK);
497 out:
498 nm_str_free(&vmname);
499 json_object_put(request);
500 }
501
nm_api_md_vmforcestop(struct json_object * request,nm_str_t * reply)502 static void nm_api_md_vmforcestop(struct json_object *request, nm_str_t *reply)
503 {
504 int rc = nm_api_check_auth(request, reply);
505 nm_mon_vms_t *vms = mon_data->vms;
506 nm_str_t vmname = NM_INIT_STR;
507 struct json_object *name;
508 bool vm_exist = false;
509 const char *name_str;
510
511 if (rc != NM_OK) {
512 goto out;
513 }
514
515 json_object_object_get_ex(request, "name", &name);
516 if (!name) {
517 nm_str_format(reply, NM_API_RET_ERR, "name param is missing");
518 goto out;
519 }
520
521 name_str = json_object_get_string(name);
522 for (size_t n = 0; n < vms->list->n_memb; n++) {
523 if (nm_str_cmp_st(nm_mon_item_get_name(vms->list, n),
524 name_str) == NM_OK) {
525 vm_exist = true;
526 break;
527 }
528 }
529
530 if (!vm_exist) {
531 nm_str_format(reply, NM_API_RET_ERR, "VM does not exists");
532 goto out;
533 }
534
535 nm_str_format(&vmname, "%s", name_str);
536 nm_qmp_vm_stop(&vmname);
537 nm_str_format(reply, "%s", NM_API_RET_OK);
538 out:
539 nm_str_free(&vmname);
540 json_object_put(request);
541 }
542
543 static void
nm_api_md_vmgetconnectport(struct json_object * request,nm_str_t * reply)544 nm_api_md_vmgetconnectport(struct json_object *request, nm_str_t *reply)
545 {
546 int rc = nm_api_check_auth(request, reply);
547 nm_mon_vms_t *vms = mon_data->vms;
548 nm_str_t query = NM_INIT_STR;
549 nm_vect_t res = NM_INIT_VECT;
550 struct json_object *name;
551 bool vm_exist = false;
552 const char *name_str;
553 uint32_t port;
554
555 if (rc != NM_OK) {
556 goto out;
557 }
558
559 json_object_object_get_ex(request, "name", &name);
560 if (!name) {
561 nm_str_format(reply, NM_API_RET_ERR, "name param is missing");
562 goto out;
563 }
564
565 name_str = json_object_get_string(name);
566 for (size_t n = 0; n < vms->list->n_memb; n++) {
567 if (nm_str_cmp_st(nm_mon_item_get_name(vms->list, n),
568 name_str) == NM_OK) {
569 vm_exist = true;
570 break;
571 }
572 }
573
574 if (!vm_exist) {
575 nm_str_format(reply, NM_API_RET_ERR, "VM does not exists");
576 goto out;
577 }
578
579 nm_str_format(&query, NM_VMCTL_GET_VNC_PORT_SQL, name_str);
580 nm_db_select(query.data, &res);
581 port = nm_str_stoui(nm_vect_str(&res, 0), 10) + NM_STARTING_VNC_PORT;
582 nm_str_format(reply, NM_API_RET_VAL_UINT, port);
583 out:
584 nm_str_free(&query);
585 nm_vect_free(&res, nm_str_vect_free_cb);
586 json_object_put(request);
587 }
588
589 #endif /* NM_WITH_REMOTE */
590 /* vim:set ts=4 sw=4: */
591