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