ui/common: Client authentication helpers
[petitboot] / ui / common / discover-client.c
1
2 #if defined(HAVE_CONFIG_H)
3 #include "config.h"
4 #endif
5
6 #include <assert.h>
7 #include <errno.h>
8 #include <unistd.h>
9 #include <stdlib.h>
10 #include <stdio.h>
11 #include <stdint.h>
12 #include <string.h>
13
14 #include <sys/socket.h>
15 #include <sys/un.h>
16 #include <asm/byteorder.h>
17
18 #include <talloc/talloc.h>
19 #include <log/log.h>
20
21 #include "discover-client.h"
22 #include "pb-protocol/pb-protocol.h"
23
24 struct discover_client {
25         int fd;
26         struct discover_client_ops ops;
27         int n_devices;
28         struct device **devices;
29         bool authenticated;
30 };
31
32 static int discover_client_destructor(void *arg)
33 {
34         struct discover_client *client = arg;
35
36         if (client->fd >= 0)
37                 close(client->fd);
38
39         return 0;
40 }
41
42 void discover_client_destroy(struct discover_client *client)
43 {
44         talloc_free(client);
45 }
46
47 static struct device *find_device(struct discover_client *client,
48                 const char *id)
49 {
50         int i;
51
52         for (i = 0; i < client->n_devices; i++) {
53                 struct device *dev = client->devices[i];
54                 if (!strcmp(dev->id, id))
55                         return dev;
56         }
57
58         return NULL;
59 }
60
61 static void device_add(struct discover_client *client, struct device *device)
62 {
63         client->n_devices++;
64         client->devices = talloc_realloc(client, client->devices,
65                         struct device *, client->n_devices);
66
67         client->devices[client->n_devices - 1] = device;
68         talloc_steal(client, device);
69         list_init(&device->boot_options);
70
71         if (client->ops.device_add)
72                 client->ops.device_add(device, client->ops.cb_arg);
73 }
74
75 static void boot_option_add(struct discover_client *client,
76                 struct boot_option *opt)
77 {
78         struct device *dev;
79
80         dev = find_device(client, opt->device_id);
81
82         /* we require that devices are already present before any boot options
83          * are added */
84         assert(dev);
85
86         talloc_steal(dev, opt);
87         list_add(&dev->boot_options, &opt->list);
88
89         if (client->ops.boot_option_add)
90                 client->ops.boot_option_add(dev, opt, client->ops.cb_arg);
91 }
92
93 static void device_remove(struct discover_client *client, const char *id)
94 {
95         struct device *device = NULL;
96         int i;
97
98         for (i = 0; i < client->n_devices; i++) {
99                 if (!strcmp(client->devices[i]->id, id)) {
100                         device = client->devices[i];
101                         break;
102                 }
103         }
104
105         if (!device)
106                 return;
107
108         /* remove the device from the client's device array */
109         client->n_devices--;
110         memmove(&client->devices[i], &client->devices[i+1],
111                         (client->n_devices - i) * sizeof(client->devices[0]));
112         client->devices = talloc_realloc(client, client->devices,
113                         struct device *, client->n_devices);
114
115         /* notify the UI */
116         client->ops.device_remove(device, client->ops.cb_arg);
117
118         talloc_free(device);
119 }
120
121 static void plugin_option_add(struct discover_client *client,
122                 struct plugin_option *opt)
123 {
124         talloc_steal(client, opt);
125
126         if (client->ops.plugin_option_add)
127                 client->ops.plugin_option_add(opt, client->ops.cb_arg);
128 }
129
130 static void plugins_remove(struct discover_client *client)
131 {
132         if (client->ops.plugins_remove)
133                 client->ops.plugins_remove(client->ops.cb_arg);
134 }
135
136 void discover_client_enumerate(struct discover_client *client)
137 {
138         struct boot_option *opt;
139         struct device *device;
140         int i;
141
142         for (i = 0; i < client->n_devices; i++) {
143                 device = client->devices[i];
144                 if (client->ops.device_add)
145                         client->ops.device_add(device, client->ops.cb_arg);
146
147                 list_for_each_entry(&device->boot_options, opt, list)
148                         if (client->ops.boot_option_add)
149                                 client->ops.boot_option_add(device, opt,
150                                                 client->ops.cb_arg);
151         }
152 }
153
154 static void update_status(struct discover_client *client,
155                 struct status *status)
156 {
157         if (client->ops.update_status)
158                 client->ops.update_status(status, client->ops.cb_arg);
159 }
160
161 static void update_sysinfo(struct discover_client *client,
162                 struct system_info *sysinfo)
163 {
164         if (client->ops.update_sysinfo)
165                 client->ops.update_sysinfo(sysinfo, client->ops.cb_arg);
166 }
167
168 static void update_config(struct discover_client *client,
169                 struct config *config)
170 {
171         if (client->ops.update_config)
172                 client->ops.update_config(config, client->ops.cb_arg);
173 }
174
175 static int discover_client_process(void *arg)
176 {
177         struct discover_client *client = arg;
178         struct pb_protocol_message *message;
179         struct auth_message *auth_msg;
180         struct plugin_option *p_opt;
181         struct system_info *sysinfo;
182         struct boot_option *opt;
183         struct status *status;
184         struct config *config;
185         struct device *dev;
186         char *dev_id;
187         void *ctx;
188         int rc;
189
190         /* We use a temporary context for processing one message; persistent
191          * data is re-parented to the client in the callbacks. */
192         ctx = talloc_new(client);
193
194         message = pb_protocol_read_message(ctx, client->fd);
195
196         if (!message)
197                 return -1;
198
199         switch (message->action) {
200         case PB_PROTOCOL_ACTION_DEVICE_ADD:
201                 dev = talloc_zero(ctx, struct device);
202                 list_init(&dev->boot_options);
203
204                 rc = pb_protocol_deserialise_device(dev, message);
205                 if (rc) {
206                         pb_log_fn("no device?\n");
207                         goto out;
208                 }
209
210                 device_add(client, dev);
211                 break;
212         case PB_PROTOCOL_ACTION_BOOT_OPTION_ADD:
213                 opt = talloc_zero(ctx, struct boot_option);
214
215                 rc = pb_protocol_deserialise_boot_option(opt, message);
216                 if (rc) {
217                         pb_log_fn("no boot_option?\n");
218                         goto out;
219                 }
220
221                 boot_option_add(client, opt);
222                 break;
223         case PB_PROTOCOL_ACTION_DEVICE_REMOVE:
224                 dev_id = pb_protocol_deserialise_string(ctx, message);
225                 if (!dev_id) {
226                         pb_log_fn("no device id?\n");
227                         goto out;
228                 }
229                 device_remove(client, dev_id);
230                 break;
231         case PB_PROTOCOL_ACTION_STATUS:
232                 status = talloc_zero(ctx, struct status);
233
234                 rc = pb_protocol_deserialise_boot_status(status, message);
235                 if (rc) {
236                         pb_log_fn("invalid status message?\n");
237                         goto out;
238                 }
239                 update_status(client, status);
240                 break;
241         case PB_PROTOCOL_ACTION_SYSTEM_INFO:
242                 sysinfo = talloc_zero(ctx, struct system_info);
243
244                 rc = pb_protocol_deserialise_system_info(sysinfo, message);
245                 if (rc) {
246                         pb_log_fn("invalid sysinfo message?\n");
247                         goto out;
248                 }
249                 update_sysinfo(client, sysinfo);
250                 break;
251         case PB_PROTOCOL_ACTION_CONFIG:
252                 config = talloc_zero(ctx, struct config);
253
254                 rc = pb_protocol_deserialise_config(config, message);
255                 if (rc) {
256                         pb_log_fn("invalid config message?\n");
257                         goto out;
258                 }
259                 update_config(client, config);
260                 break;
261         case PB_PROTOCOL_ACTION_PLUGIN_OPTION_ADD:
262                 p_opt = talloc_zero(ctx, struct plugin_option);
263
264                 rc = pb_protocol_deserialise_plugin_option(p_opt, message);
265                 if (rc) {
266                         pb_log_fn("no plugin_option?\n");
267                         goto out;
268                 }
269
270                 plugin_option_add(client, p_opt);
271                 break;
272         case PB_PROTOCOL_ACTION_PLUGINS_REMOVE:
273                 plugins_remove(client);
274                 break;
275         case PB_PROTOCOL_ACTION_AUTHENTICATE:
276                 auth_msg = talloc_zero(ctx, struct auth_message);
277
278                 rc = pb_protocol_deserialise_authenticate(auth_msg, message);
279                 if (rc || auth_msg->op != AUTH_MSG_RESPONSE) {
280                         pb_log("%s: invalid auth message? (%d)\n",
281                                         __func__, rc);
282                         goto out;
283                 }
284
285                 pb_log("Client %sauthenticated by server\n",
286                                 client->authenticated ? "" : "un");
287                 client->authenticated = auth_msg->authenticated;
288                 break;
289         default:
290                 pb_log_fn("unknown action %d\n", message->action);
291         }
292
293 out:
294         talloc_free(ctx);
295
296         return 0;
297 }
298
299 struct discover_client* discover_client_init(struct waitset *waitset,
300         const struct discover_client_ops *ops, void *cb_arg)
301 {
302         struct discover_client *client;
303         struct sockaddr_un addr;
304
305         client = talloc(NULL, struct discover_client);
306         if (!client)
307                 return NULL;
308
309         memcpy(&client->ops, ops, sizeof(client->ops));
310         client->ops.cb_arg = cb_arg;
311
312         client->fd = socket(AF_UNIX, SOCK_STREAM, 0);
313         if (client->fd < 0) {
314                 pb_log_fn("socket: %s\n", strerror(errno));
315                 goto out_err;
316         }
317
318         talloc_set_destructor(client, discover_client_destructor);
319
320         client->n_devices = 0;
321         client->devices = NULL;
322
323         addr.sun_family = AF_UNIX;
324         strcpy(addr.sun_path, PB_SOCKET_PATH);
325
326         if (connect(client->fd, (struct sockaddr *)&addr, sizeof(addr))) {
327                 pb_log_fn("connect: %s\n", strerror(errno));
328                 goto out_err;
329         }
330
331         waiter_register_io(waitset, client->fd, WAIT_IN,
332                         discover_client_process, client);
333
334         /* Assume this client can't make changes if crypt support is enabled */
335 #ifdef CRYPT_SUPPORT
336         client->authenticated = false;
337 #else
338         client->authenticated = true;
339 #endif
340
341         return client;
342
343 out_err:
344         talloc_free(client);
345         return NULL;
346 }
347
348 /* accessors for discovered devices */
349 int discover_client_device_count(struct discover_client *client)
350 {
351         return client->n_devices;
352 }
353
354 struct device *discover_client_get_device(struct discover_client *client,
355                 int index)
356 {
357         if (index < 0 || index >= client->n_devices)
358                 return NULL;
359
360         return client->devices[index];
361 }
362
363 bool discover_client_authenticated(struct discover_client *client)
364 {
365         return client->authenticated;
366 }
367
368 static void create_boot_command(struct boot_command *command,
369                 const struct device *device __attribute__((unused)),
370                 const struct boot_option *boot_option,
371                 const struct pb_boot_data *data)
372 {
373         command->option_id = boot_option ? boot_option->id : NULL;
374         command->boot_image_file = data->image;
375         command->initrd_file = data->initrd;
376         command->dtb_file = data->dtb;
377         command->boot_args = data->args;
378         command->args_sig_file = data->args_sig_file;
379         command->console = ttyname(STDIN_FILENO);
380 }
381
382 int discover_client_boot(struct discover_client *client,
383                 const struct device *device,
384                 const struct boot_option *boot_option,
385                 const struct pb_boot_data *data)
386 {
387         struct pb_protocol_message *message;
388         struct boot_command boot_command;
389         int len, rc;
390
391         create_boot_command(&boot_command, device, boot_option, data);
392
393         len = pb_protocol_boot_len(&boot_command);
394
395         message = pb_protocol_create_message(client,
396                         PB_PROTOCOL_ACTION_BOOT, len);
397
398         if (!message)
399                 return -1;
400
401         pb_protocol_serialise_boot_command(&boot_command,
402                         message->payload, len);
403
404         rc = pb_protocol_write_message(client->fd, message);
405
406         return rc;
407 }
408
409 int discover_client_cancel_default(struct discover_client *client)
410 {
411         struct pb_protocol_message *message;
412
413         message = pb_protocol_create_message(client,
414                         PB_PROTOCOL_ACTION_CANCEL_DEFAULT, 0);
415
416         if (!message)
417                 return -1;
418
419         return pb_protocol_write_message(client->fd, message);
420 }
421
422 int discover_client_send_reinit(struct discover_client *client)
423 {
424         struct pb_protocol_message *message;
425
426         message = pb_protocol_create_message(client,
427                         PB_PROTOCOL_ACTION_REINIT, 0);
428
429         if (!message)
430                 return -1;
431
432         return pb_protocol_write_message(client->fd, message);
433 }
434
435 int discover_client_send_config(struct discover_client *client,
436                 struct config *config)
437 {
438         struct pb_protocol_message *message;
439         int len;
440
441         len = pb_protocol_config_len(config);
442
443         message = pb_protocol_create_message(client,
444                                 PB_PROTOCOL_ACTION_CONFIG, len);
445         if (!message)
446                 return -1;
447
448         pb_protocol_serialise_config(config, message->payload, len);
449
450         return pb_protocol_write_message(client->fd, message);
451 }
452
453 int discover_client_send_url(struct discover_client *client,
454                 char *url)
455 {
456         struct pb_protocol_message *message;
457         int len;
458
459         len = pb_protocol_url_len(url);
460
461         message = pb_protocol_create_message(client,
462                                 PB_PROTOCOL_ACTION_ADD_URL, len);
463         if (!message)
464                 return -1;
465
466         pb_protocol_serialise_url(url, message->payload, len);
467
468         return pb_protocol_write_message(client->fd, message);
469 }
470
471 int discover_client_send_plugin_install(struct discover_client *client,
472                 char *file)
473 {
474         struct pb_protocol_message *message;
475         int len;
476
477         len = pb_protocol_url_len(file);
478
479         message = pb_protocol_create_message(client,
480                                 PB_PROTOCOL_ACTION_PLUGIN_INSTALL, len);
481         if (!message)
482                 return -1;
483
484         pb_protocol_serialise_url(file, message->payload, len);
485
486         return pb_protocol_write_message(client->fd, message);
487 }
488
489 int discover_client_send_temp_autoboot(struct discover_client *client,
490                 const struct autoboot_option *opt)
491 {
492         struct pb_protocol_message *message;
493         int len;
494
495         len = pb_protocol_temp_autoboot_len(opt);
496
497         message = pb_protocol_create_message(client,
498                         PB_PROTOCOL_ACTION_TEMP_AUTOBOOT, len);
499         if (!message)
500                 return -1;
501
502         pb_protocol_serialise_temp_autoboot(opt, message->payload, len);
503
504         return pb_protocol_write_message(client->fd, message);
505 }
506
507 int discover_client_send_authenticate(struct discover_client *client,
508                 char *password)
509 {
510         struct pb_protocol_message *message;
511         struct auth_message auth_msg;
512         int len;
513
514         auth_msg.op = AUTH_MSG_REQUEST;
515         auth_msg.password = password;
516
517         len = pb_protocol_authenticate_len(&auth_msg);
518
519         message = pb_protocol_create_message(client,
520                                 PB_PROTOCOL_ACTION_AUTHENTICATE, len);
521         if (!message)
522                 return -1;
523
524         pb_log("serialising auth message..\n");
525         pb_protocol_serialise_authenticate(&auth_msg, message->payload, len);
526
527         pb_log("sending auth message..\n");
528         return pb_protocol_write_message(client->fd, message);
529 }
530
531 int discover_client_send_set_password(struct discover_client *client,
532                 char *password, char *new_password)
533 {
534         struct pb_protocol_message *message;
535         struct auth_message auth_msg;
536         int len;
537
538         auth_msg.op = AUTH_MSG_SET;
539         auth_msg.set_password.password = password;
540         auth_msg.set_password.new_password = new_password;
541
542         len = pb_protocol_authenticate_len(&auth_msg);
543
544         message = pb_protocol_create_message(client,
545                                 PB_PROTOCOL_ACTION_AUTHENTICATE, len);
546         if (!message)
547                 return -1;
548
549         pb_log("serialising auth message..\n");
550         pb_protocol_serialise_authenticate(&auth_msg, message->payload, len);
551
552         pb_log("sending auth message..\n");
553         return pb_protocol_write_message(client->fd, message);
554 }