]> err.no Git - yubikey-personalization/blob - ykpersonalize.c
Merge tag 'v1.15.1'
[yubikey-personalization] / ykpersonalize.c
1 /* -*- mode:C; c-file-style: "bsd" -*- */
2 /*
3  * Copyright (c) 2008-2014 Yubico AB
4  * Copyright (c) 2010 Tollef Fog Heen <tfheen@err.no>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are
9  * met:
10  *
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *
14  *     * Redistributions in binary form must reproduce the above
15  *       copyright notice, this list of conditions and the following
16  *       disclaimer in the documentation and/or other materials provided
17  *       with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <stddef.h>
35 #include <string.h>
36 #include <unistd.h>
37 #include <errno.h>
38
39 #include <ykpers.h>
40 #include <ykdef.h>
41
42 #include "ykpers-args.h"
43
44 int main(int argc, char **argv)
45 {
46         FILE *inf = NULL; const char *infname = NULL;
47         FILE *outf = NULL; const char *outfname = NULL;
48         int data_format = YKP_FORMAT_LEGACY;
49         bool verbose = false;
50         char keylocation = 0;
51         bool use_access_code = false;
52         unsigned char access_code[256];
53         unsigned char scan_codes[sizeof(SCAN_MAP)];
54         YK_KEY *yk = 0;
55         YKP_CONFIG *cfg = ykp_alloc();
56         YK_STATUS *st = ykds_alloc();
57         bool autocommit = false;
58         char data[1024];
59         bool dry_run = false;
60
61         /* Options */
62         char ndef_string[128] = {0};
63         char ndef_type = 0;
64         unsigned char usb_mode = 0;
65         unsigned char cr_timeout = 0;
66         unsigned char autoeject_timeout = 0;
67         int num_modes_seen = 0;
68         bool zap = false;
69
70         bool error = false;
71         int exit_code = 0;
72
73         ykp_errno = 0;
74         yk_errno = 0;
75
76         /* Assume the worst */
77         error = true;
78
79         if (!yk_init()) {
80                 exit_code = 1;
81                 goto err;
82         }
83
84         if (argc == 2 && strcmp (argv[1], "-h") == 0) {
85                 fputs(usage, stderr);
86                 goto err;
87         }
88
89         if (!(yk = yk_open_first_key())) {
90                 exit_code = 1;
91                 goto err;
92         }
93
94         if (!yk_get_status(yk, st)) {
95                 exit_code = 1;
96                 goto err;
97         }
98
99         printf("Firmware version %d.%d.%d Touch level %d ",
100                ykds_version_major(st),
101                ykds_version_minor(st),
102                ykds_version_build(st),
103                ykds_touch_level(st));
104         if (ykds_pgm_seq(st))
105                 printf("Program sequence %d\n",
106                        ykds_pgm_seq(st));
107         else
108                 printf("Unconfigured\n");
109
110         if (!(yk_check_firmware_version2(st))) {
111                 if (yk_errno == YK_EFIRMWARE) {
112                         printf("Unsupported firmware revision - some "
113                                "features may not be available\n"
114                                "Please see \n"
115                                "http://opensource.yubico.com/yubikey-personalization/doc/Compatibility.html\n"
116                                "for more information.\n");
117                 } else {
118                         goto err;
119                 }
120         }
121
122         /* Parse all arguments in a testable way */
123         if (! args_to_config(argc, argv, cfg, yk,
124                              &infname, &outfname,
125                              &data_format, &autocommit,
126                              st, &verbose, &dry_run,
127                              access_code, &use_access_code,
128                              &keylocation, &ndef_type, ndef_string,
129                              &usb_mode, &zap, scan_codes, &cr_timeout,
130                              &autoeject_timeout, &num_modes_seen, &exit_code)) {
131                 goto err;
132         }
133
134         if (verbose && (ykds_version_major(st) > 2 ||
135                         (ykds_version_major(st) == 2 &&
136                          ykds_version_minor(st) >= 2) ||
137                         (ykds_version_major(st) == 2 && // neo has serial functions
138                          ykds_version_minor(st) == 1 &&
139                          ykds_version_build(st) >= 4))) {
140                 unsigned int serial;
141                 if (! yk_get_serial(yk, 0, 0, &serial)) {
142                         printf ("Failed to read serial number (serial-api-visible disabled?).\n");
143
144                 } else {
145                         printf ("Serial number : %i\n", serial);
146                 }
147         }
148
149         printf ("\n");
150
151         if (infname) {
152                 if (strcmp(infname, "-") == 0)
153                         inf = stdin;
154                 else
155                         inf = fopen(infname, "r");
156                 if (inf == NULL) {
157                         fprintf(stderr,
158                                 "Couldn't open %s for reading: %s\n",
159                                 infname,
160                                 strerror(errno));
161                         exit_code = 1;
162                         goto err;
163                 }
164         }
165
166         if (outfname) {
167                 if (strcmp(outfname, "-") == 0)
168                         outf = stdout;
169                 else
170                         outf = fopen(outfname, "w");
171                 if (outf == NULL) {
172                         fprintf(stderr,
173                                 "Couldn't open %s for writing: %s\n",
174                                 outfname,
175                                 strerror(errno));
176                         exit(1);
177                 }
178         }
179
180         if (inf) {
181                 if(!ykp_clear_config(cfg))
182                         goto err;
183                 if(!fread(data, 1, 1024, inf))
184                         goto err;
185                 if (!ykp_import_config(cfg, data, strlen(data), data_format))
186                         goto err;
187         } else if (! zap && (ykp_command(cfg) == SLOT_CONFIG || ykp_command(cfg) == SLOT_CONFIG2)) {
188                 int key_bytes = ykp_get_supported_key_length(cfg);
189                 char keybuf[42];
190                 size_t keylen;
191                 if(keylocation == 2) {
192                         if(key_bytes == 20) {
193                                 fprintf(stderr, " HMAC key, 20 bytes (40 characters hex) : ");
194                         } else {
195                                 fprintf(stderr, " AES key, 16 bytes (32 characters hex) : ");
196                         }
197                         fflush(stderr);
198                         if(!fgets(keybuf, sizeof(keybuf), stdin)) {
199                                 printf("error?\n");
200                                 perror ("fgets");
201                                 exit_code = 1;
202                                 goto err;
203                         }
204                         keylen = strnlen(keybuf, sizeof(keybuf));
205                         if(keybuf[keylen - 1] == '\n') {
206                                 keybuf[keylen - 1] = '\0';
207                         }
208                         if(key_bytes == 20) {
209                                 if(ykp_HMAC_key_from_hex(cfg, keybuf)) {
210                                         goto err;
211                                 }
212                         } else {
213                                 if(ykp_AES_key_from_hex(cfg, keybuf)) {
214                                         goto err;
215                                 }
216                         }
217                 } else if(keylocation == 0) {
218                         const char *random_places[] = {
219                                 "/dev/srandom",
220                                 "/dev/urandom",
221                                 "/dev/random",
222                                 0
223                         };
224                         const char **random_place;
225                         size_t read_bytes = 0;
226
227                         for (random_place = random_places; *random_place; random_place++) {
228                                 FILE *random_file = fopen(*random_place, "r");
229                                 if (random_file) {
230                                         read_bytes = 0;
231
232                                         while (read_bytes < key_bytes) {
233                                                 size_t n = fread(&keybuf[read_bytes], 1,
234                                                                 key_bytes - read_bytes, random_file);
235                                                 read_bytes += n;
236                                         }
237
238                                         fclose(random_file);
239                                         break;
240                                 }
241                         }
242                         if(read_bytes < key_bytes) {
243                                 ykp_errno = YKP_ENORANDOM;
244                                 goto err;
245                         }
246                         if(key_bytes == 20) {
247                                 if(ykp_HMAC_key_from_raw(cfg, keybuf)) {
248                                         goto err;
249                                 }
250                         } else {
251                                 if(ykp_AES_key_from_raw(cfg, keybuf)) {
252                                         goto err;
253                                 }
254                         }
255                 }
256         }
257
258         if (outf) {
259                 if(!(ykp_export_config(cfg, data, 1024, data_format))) {
260                         goto err;
261                 }
262                 if(!(fwrite(data, 1, strlen(data), outf))) {
263                         goto err;
264                 }
265         } else {
266                 char commitbuf[256]; size_t commitlen;
267
268                 if (ykp_command(cfg) == SLOT_SWAP) {
269                         fprintf(stderr, "Configuration in slot 1 and 2 will be swapped\n");
270                 } else if(ykp_command(cfg) == SLOT_NDEF || ykp_command(cfg) == SLOT_NDEF2) {
271                         fprintf(stderr, "New NDEF will be written as:\n%s\n", ndef_string);
272                 } else if(ykp_command(cfg) == SLOT_DEVICE_CONFIG) {
273                         fprintf(stderr, "The USB mode will be set to: 0x%x\n", usb_mode);
274                         if(num_modes_seen > 1) {
275                                 fprintf(stderr, "The challenge response timeout will be set to: %d\n", cr_timeout);
276                                 if(num_modes_seen > 2) {
277                                         fprintf(stderr, "The smartcard autoeject timeout will be set to: %d\n", autoeject_timeout);
278                                 }
279                         }
280                 } else if(ykp_command(cfg) == SLOT_SCAN_MAP) {
281                         fprintf(stderr, "A new scanmap will be written.\n");
282                 } else if(zap) {
283                         fprintf(stderr, "Configuration in slot %d will be deleted\n", ykp_config_num(cfg));
284                 } else {
285                         if (ykp_command(cfg) == SLOT_CONFIG || ykp_command(cfg) == SLOT_CONFIG2) {
286                                 fprintf(stderr, "Configuration data to be written to key configuration %d:\n\n", ykp_config_num(cfg));
287                         } else {
288                                 fprintf(stderr, "Configuration data to be updated in key configuration %d:\n\n", ykp_command(cfg) == SLOT_UPDATE1 ? 1 : 2);
289                         }
290                         ykp_export_config(cfg, data, 1024, YKP_FORMAT_LEGACY);
291                         fwrite(data, 1, strlen(data), stderr);
292                 }
293                 fprintf(stderr, "\nCommit? (y/n) [n]: ");
294                 if (autocommit) {
295                         strcpy(commitbuf, "yes");
296                         puts(commitbuf);
297                 } else {
298                         if (!fgets(commitbuf, sizeof(commitbuf), stdin))
299                         {
300                                 perror ("fgets");
301                                 goto err;
302                         }
303                 }
304                 commitlen = strlen(commitbuf);
305                 if (commitbuf[commitlen - 1] == '\n')
306                         commitbuf[commitlen - 1] = '\0';
307                 if (strcmp(commitbuf, "y") == 0
308                     || strcmp(commitbuf, "yes") == 0) {
309                         exit_code = 2;
310
311                         if (verbose)
312                                 printf("Attempting to write configuration to the yubikey...");
313                         if (dry_run) {
314                                 printf("Not writing anything to key due to dry_run requested.\n");
315                         }
316                         else if(ykp_command(cfg) == SLOT_NDEF || ykp_command(cfg) == SLOT_NDEF2) {
317                                 YK_NDEF *ndef = ykp_alloc_ndef();
318                                 int confnum = 1;
319                                 if(ndef_type == 'U') {
320                                         ykp_construct_ndef_uri(ndef, ndef_string);
321                                 } else if(ndef_type == 'T') {
322                                         ykp_construct_ndef_text(ndef, ndef_string, "en", false);
323                                 }
324                                 if(use_access_code) {
325                                         ykp_set_ndef_access_code(ndef, access_code);
326                                 }
327                                 if(ykp_command(cfg) == SLOT_NDEF2) {
328                                         confnum = 2;
329                                 }
330                                 if (!yk_write_ndef2(yk, ndef, confnum)) {
331                                         if (verbose)
332                                                 printf(" failure\n");
333                                         goto err;
334                                 }
335                                 ykp_free_ndef(ndef);
336                         } else if(ykp_command(cfg) == SLOT_DEVICE_CONFIG) {
337                                 YK_DEVICE_CONFIG *device_config = ykp_alloc_device_config();
338                                 ykp_set_device_mode(device_config, usb_mode);
339                                 if(num_modes_seen > 1) {
340                                         ykp_set_device_chalresp_timeout(device_config, cr_timeout);
341                                         if(num_modes_seen > 2) {
342                                                 ykp_set_device_autoeject_time(device_config, autoeject_timeout);
343                                         }
344                                 }
345
346                                 if(!yk_write_device_config(yk, device_config)) {
347                                         if(verbose)
348                                                 printf(" failure\n");
349                                         goto err;
350                                 }
351                                 ykp_free_device_config(device_config);
352
353
354                         } else if(ykp_command(cfg) == SLOT_SCAN_MAP) {
355                                 if(!yk_write_scan_map(yk, scan_codes)) {
356                                         if(verbose)
357                                                 printf(" failure\n");
358                                         goto err;
359                                 }
360                         } else {
361                                 YK_CONFIG *ycfg = NULL;
362                                 /* if we're deleting a slot we send the configuration as NULL */
363                                 if (!zap) {
364                                         ycfg = ykp_core_config(cfg);
365                                 }
366                                 if (!yk_write_command(yk,
367                                                         ycfg, ykp_command(cfg),
368                                                         use_access_code ? access_code : NULL)) {
369                                         if (verbose)
370                                                 printf(" failure\n");
371                                         goto err;
372                                 }
373                         }
374
375                         if (verbose && !dry_run)
376                                 printf(" success\n");
377                 }
378         }
379
380         exit_code = 0;
381         error = false;
382
383 err:
384         if (error) {
385                 report_yk_error();
386         }
387
388         if (st)
389                 free(st);
390         if (inf)
391                 fclose(inf);
392         if (outf)
393                 fclose(outf);
394
395         if (yk && !yk_close_key(yk)) {
396                 report_yk_error();
397                 exit_code = 2;
398         }
399
400         if (!yk_release()) {
401                 report_yk_error();
402                 exit_code = 2;
403         }
404
405         if (cfg)
406                 ykp_free_config(cfg);
407
408         exit(exit_code);
409 }