/src/opensc/src/tools/util.c
Line | Count | Source |
1 | | /* |
2 | | * util.c: utility functions used by OpenSC command line tools. |
3 | | * |
4 | | * Copyright (C) 2011 OpenSC Project developers |
5 | | * |
6 | | * This library is free software; you can redistribute it and/or |
7 | | * modify it under the terms of the GNU Lesser General Public |
8 | | * License as published by the Free Software Foundation; either |
9 | | * version 2.1 of the License, or (at your option) any later version. |
10 | | * |
11 | | * This library is distributed in the hope that it will be useful, |
12 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | | * Lesser General Public License for more details. |
15 | | * |
16 | | * You should have received a copy of the GNU Lesser General Public |
17 | | * License along with this library; if not, write to the Free Software |
18 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
19 | | */ |
20 | | |
21 | | #include "config.h" |
22 | | |
23 | | #include <stdio.h> |
24 | | #include <stdlib.h> |
25 | | #include <stdarg.h> |
26 | | #ifndef _WIN32 |
27 | | #include <termios.h> |
28 | | #else |
29 | | #include <conio.h> |
30 | | #endif |
31 | | #include <ctype.h> |
32 | | #include "util.h" |
33 | | #include "ui/notify.h" |
34 | | #include "common/compat_strlcat.h" |
35 | | |
36 | | int |
37 | | is_string_valid_atr(const char *atr_str) |
38 | 0 | { |
39 | 0 | unsigned char atr[SC_MAX_ATR_SIZE]; |
40 | 0 | size_t atr_len = sizeof(atr); |
41 | |
|
42 | 0 | if (sc_hex_to_bin(atr_str, atr, &atr_len)) |
43 | 0 | return 0; |
44 | 0 | if (atr_len < 2) |
45 | 0 | return 0; |
46 | 0 | if (atr[0] != 0x3B && atr[0] != 0x3F) |
47 | 0 | return 0; |
48 | 0 | return 1; |
49 | 0 | } |
50 | | |
51 | | int util_connect_reader (sc_context_t *ctx, sc_reader_t **reader, |
52 | | const char *reader_id, int do_wait) |
53 | 0 | { |
54 | 0 | struct sc_reader *found = NULL; |
55 | 0 | int r; |
56 | |
|
57 | 0 | setbuf(stderr, NULL); |
58 | 0 | setbuf(stdout, NULL); |
59 | |
|
60 | 0 | sc_notify_init(); |
61 | |
|
62 | 0 | if (do_wait) { |
63 | 0 | unsigned int event = 0; |
64 | |
|
65 | 0 | if (sc_ctx_get_reader_count(ctx) == 0) { |
66 | 0 | fprintf(stderr, "Waiting for a reader to be attached...\n"); |
67 | 0 | r = sc_wait_for_event(ctx, SC_EVENT_READER_ATTACHED|SC_EVENT_CARD_INSERTED, |
68 | 0 | &found, &event, -1, NULL); |
69 | 0 | if (r < 0) { |
70 | 0 | fprintf(stderr, "Error while waiting for a reader: %s\n", sc_strerror(r)); |
71 | 0 | return r; |
72 | 0 | } |
73 | 0 | r = sc_ctx_detect_readers(ctx); |
74 | 0 | if (r < 0) { |
75 | 0 | fprintf(stderr, "Error while refreshing readers: %s\n", sc_strerror(r)); |
76 | 0 | return r; |
77 | 0 | } |
78 | 0 | } |
79 | 0 | if (event & SC_EVENT_CARD_INSERTED) { |
80 | 0 | *reader = found; |
81 | 0 | } else { |
82 | 0 | fprintf(stderr, "Waiting for a card to be inserted...\n"); |
83 | 0 | r = sc_wait_for_event(ctx, SC_EVENT_CARD_INSERTED, &found, &event, -1, NULL); |
84 | 0 | if (r < 0) { |
85 | 0 | fprintf(stderr, "Error while waiting for a card: %s\n", sc_strerror(r)); |
86 | 0 | return r; |
87 | 0 | } |
88 | 0 | *reader = found; |
89 | 0 | } |
90 | 0 | } |
91 | 0 | else if (sc_ctx_get_reader_count(ctx) == 0) { |
92 | 0 | fprintf(stderr, "No smart card readers found.\n"); |
93 | 0 | return SC_ERROR_NO_READERS_FOUND; |
94 | 0 | } |
95 | 0 | else { |
96 | 0 | if (!reader_id) { |
97 | 0 | unsigned int i; |
98 | | /* Automatically try to skip to a reader with a card if reader not specified */ |
99 | 0 | for (i = 0; i < sc_ctx_get_reader_count(ctx); i++) { |
100 | 0 | *reader = sc_ctx_get_reader(ctx, i); |
101 | 0 | if (sc_detect_card_presence(*reader) & SC_READER_CARD_PRESENT) { |
102 | 0 | fprintf(stderr, "Using reader with a card: %s\n", (*reader)->name); |
103 | 0 | goto autofound; |
104 | 0 | } |
105 | 0 | } |
106 | | /* If no reader had a card, default to the first reader */ |
107 | 0 | *reader = sc_ctx_get_reader(ctx, 0); |
108 | 0 | } |
109 | 0 | else { |
110 | | /* If the reader identifier looks like an ATR, try to find the reader with that card */ |
111 | 0 | if (is_string_valid_atr(reader_id)) { |
112 | 0 | unsigned char atr_buf[SC_MAX_ATR_SIZE]; |
113 | 0 | size_t atr_buf_len = sizeof(atr_buf); |
114 | 0 | unsigned int i; |
115 | |
|
116 | 0 | sc_hex_to_bin(reader_id, atr_buf, &atr_buf_len); |
117 | | /* Loop readers, looking for a card with ATR */ |
118 | 0 | for (i = 0; i < sc_ctx_get_reader_count(ctx); i++) { |
119 | 0 | struct sc_reader *rdr = sc_ctx_get_reader(ctx, i); |
120 | |
|
121 | 0 | if (!(sc_detect_card_presence(rdr) & SC_READER_CARD_PRESENT)) |
122 | 0 | continue; |
123 | 0 | else if (rdr->atr.len != atr_buf_len) |
124 | 0 | continue; |
125 | 0 | else if (memcmp(rdr->atr.value, atr_buf, rdr->atr.len)) |
126 | 0 | continue; |
127 | | |
128 | 0 | fprintf(stderr, "Matched ATR in reader: %s\n", rdr->name); |
129 | 0 | *reader = rdr; |
130 | 0 | goto autofound; |
131 | 0 | } |
132 | 0 | } |
133 | 0 | else { |
134 | 0 | char *endptr = NULL; |
135 | 0 | long num; |
136 | |
|
137 | 0 | errno = 0; |
138 | 0 | num = strtol(reader_id, &endptr, 0); |
139 | 0 | if (!errno && endptr && *endptr == '\0') |
140 | 0 | *reader = sc_ctx_get_reader(ctx, (unsigned)num); |
141 | 0 | else |
142 | 0 | *reader = sc_ctx_get_reader_by_name(ctx, reader_id); |
143 | 0 | } |
144 | 0 | } |
145 | 0 | autofound: |
146 | 0 | if (!(*reader)) { |
147 | 0 | fprintf(stderr, "Reader \"%s\" not found (%d reader(s) detected)\n", |
148 | 0 | reader_id, sc_ctx_get_reader_count(ctx)); |
149 | 0 | return SC_ERROR_READER; |
150 | 0 | } |
151 | | |
152 | 0 | if (sc_detect_card_presence(*reader) <= 0) { |
153 | 0 | fprintf(stderr, "Card not present.\n"); |
154 | 0 | return SC_ERROR_CARD_NOT_PRESENT; |
155 | 0 | } |
156 | 0 | } |
157 | 0 | return SC_SUCCESS; |
158 | 0 | } |
159 | | int |
160 | | util_connect_card_ex(sc_context_t *ctx, sc_card_t **cardp, |
161 | | const char *reader_id, int do_wait, int do_lock) |
162 | 0 | { |
163 | 0 | struct sc_reader *reader = NULL; |
164 | 0 | struct sc_card *card = NULL; |
165 | 0 | int r; |
166 | |
|
167 | 0 | r = util_connect_reader(ctx, &reader, reader_id, do_wait); |
168 | 0 | if(r) |
169 | 0 | return r; |
170 | 0 | if (ctx->debug) |
171 | 0 | printf("Connecting to card in reader %s...\n", reader->name); |
172 | 0 | r = sc_connect_card(reader, &card); |
173 | 0 | if (r < 0) { |
174 | 0 | fprintf(stderr, "Failed to connect to card: %s\n", sc_strerror(r)); |
175 | 0 | return r; |
176 | 0 | } |
177 | | |
178 | 0 | if (ctx->debug) |
179 | 0 | printf("Using card driver %s.\n", card->driver->name); |
180 | |
|
181 | 0 | if (do_lock) { |
182 | 0 | r = sc_lock(card); |
183 | 0 | if (r < 0) { |
184 | 0 | fprintf(stderr, "Failed to lock card: %s\n", sc_strerror(r)); |
185 | 0 | sc_disconnect_card(card); |
186 | 0 | return r; |
187 | 0 | } |
188 | 0 | } |
189 | | |
190 | 0 | *cardp = card; |
191 | 0 | return SC_SUCCESS; |
192 | 0 | } |
193 | | |
194 | | int |
195 | | util_connect_card(sc_context_t *ctx, sc_card_t **cardp, |
196 | | const char *reader_id, int do_wait) |
197 | 0 | { |
198 | 0 | return util_connect_card_ex(ctx, cardp, reader_id, do_wait, 1); |
199 | 0 | } |
200 | | |
201 | | void util_print_binary(FILE *f, const u8 *buf, size_t count) |
202 | 0 | { |
203 | 0 | size_t i; |
204 | |
|
205 | 0 | for (i = 0; i < count; i++) { |
206 | 0 | unsigned char c = buf[i]; |
207 | 0 | const char *format; |
208 | 0 | if (!isprint(c)) |
209 | 0 | format = "\\x%02X"; |
210 | 0 | else |
211 | 0 | format = "%c"; |
212 | 0 | fprintf(f, format, c); |
213 | 0 | } |
214 | 0 | (void) fflush(f); |
215 | 0 | } |
216 | | |
217 | | void util_hex_dump(FILE *f, const u8 *in, size_t len, const char *sep) |
218 | 0 | { |
219 | 0 | size_t i; |
220 | |
|
221 | 0 | for (i = 0; i < len; i++) { |
222 | 0 | if (sep != NULL && i) |
223 | 0 | fprintf(f, "%s", sep); |
224 | 0 | fprintf(f, "%02X", in[i]); |
225 | 0 | } |
226 | 0 | } |
227 | | |
228 | | void util_hex_dump_asc(FILE *f, const u8 *in, size_t count, int addr) |
229 | 3.83k | { |
230 | 3.83k | int lines = 0; |
231 | | |
232 | 79.0k | while (count) { |
233 | 75.2k | char ascbuf[17]; |
234 | 75.2k | size_t i; |
235 | | |
236 | 75.2k | if (addr >= 0) { |
237 | 0 | fprintf(f, "%08X: ", addr); |
238 | 0 | addr += 16; |
239 | 0 | } |
240 | 1.23M | for (i = 0; i < count && i < 16; i++) { |
241 | 1.16M | fprintf(f, "%02X ", *in); |
242 | 1.16M | if (isprint(*in)) |
243 | 620k | ascbuf[i] = *in; |
244 | 542k | else |
245 | 542k | ascbuf[i] = '.'; |
246 | 1.16M | in++; |
247 | 1.16M | } |
248 | 75.2k | count -= i; |
249 | 75.2k | ascbuf[i] = 0; |
250 | 81.4k | for (; i < 16 && lines; i++) |
251 | 6.26k | fprintf(f, " "); |
252 | 75.2k | fprintf(f, "%s\n", ascbuf); |
253 | 75.2k | lines++; |
254 | 75.2k | } |
255 | 3.83k | } |
256 | | |
257 | | void |
258 | | util_print_usage(const char *app_name, const struct option options[], |
259 | | const char *option_help[], const char *args) |
260 | 83 | { |
261 | 83 | int i; |
262 | 83 | int header_shown = 0; |
263 | | |
264 | 83 | if (args) |
265 | 0 | printf("Usage: %s [OPTIONS] %s\n", app_name, args); |
266 | 83 | else |
267 | 83 | printf("Usage: %s [OPTIONS]\n", app_name); |
268 | | |
269 | 1.16k | for (i = 0; options[i].name; i++) { |
270 | 1.07k | char buf[40]; |
271 | 1.07k | const char *arg_str; |
272 | | |
273 | | /* Skip "hidden" options */ |
274 | 1.07k | if (option_help[i] == NULL) |
275 | 0 | continue; |
276 | | |
277 | 1.07k | if (!header_shown++) |
278 | 83 | printf("Options:\n"); |
279 | | |
280 | 1.07k | switch (options[i].has_arg) { |
281 | 747 | case 1: |
282 | 747 | arg_str = " <arg>"; |
283 | 747 | break; |
284 | 0 | case 2: |
285 | 0 | arg_str = " [arg]"; |
286 | 0 | break; |
287 | 332 | default: |
288 | 332 | arg_str = ""; |
289 | 332 | break; |
290 | 1.07k | } |
291 | 1.07k | if (isascii(options[i].val) && |
292 | 1.07k | isprint(options[i].val) && !isspace(options[i].val)) |
293 | 996 | sprintf(buf, "-%c, --%s%s", options[i].val, options[i].name, arg_str); |
294 | 83 | else |
295 | 83 | sprintf(buf, " --%s%s", options[i].name, arg_str); |
296 | | |
297 | | /* print the line - wrap if necessary */ |
298 | 1.07k | if (strlen(buf) > 28) { |
299 | 0 | printf(" %s\n", buf); |
300 | 0 | buf[0] = '\0'; |
301 | 0 | } |
302 | 1.07k | printf(" %-28s %s\n", buf, option_help[i]); |
303 | 1.07k | } |
304 | 83 | } |
305 | | |
306 | | NORETURN void |
307 | | util_print_usage_and_die(const char *app_name, const struct option options[], |
308 | | const char *option_help[], const char *args) |
309 | 0 | { |
310 | 0 | util_print_usage(app_name, options, option_help, args); |
311 | 0 | exit(2); |
312 | 0 | } |
313 | | |
314 | | int util_list_card_drivers(const sc_context_t *ctx) |
315 | 0 | { |
316 | 0 | int i; |
317 | |
|
318 | 0 | if (ctx == NULL) { |
319 | 0 | fprintf(stderr, "Unable to get card drivers!\n"); |
320 | 0 | return 1; |
321 | 0 | } |
322 | 0 | if (ctx->card_drivers[0] == NULL) { |
323 | 0 | fprintf(stderr, "No card drivers installed!\n"); |
324 | 0 | return 1; |
325 | 0 | } |
326 | 0 | printf("Available card drivers:\n"); |
327 | 0 | for (i = 0; ctx->card_drivers[i] != NULL; i++) { |
328 | 0 | printf(" %-16s %s\n", ctx->card_drivers[i]->short_name, |
329 | 0 | ctx->card_drivers[i]->name); |
330 | 0 | } |
331 | 0 | return 0; |
332 | 0 | } |
333 | | |
334 | | const char * util_acl_to_str(const sc_acl_entry_t *e) |
335 | 0 | { |
336 | 0 | static char line[80], buf[20]; |
337 | 0 | unsigned int acl; |
338 | |
|
339 | 0 | if (e == NULL) |
340 | 0 | return "N/A"; |
341 | 0 | line[0] = 0; |
342 | 0 | while (e != NULL) { |
343 | 0 | acl = e->method; |
344 | |
|
345 | 0 | switch (acl) { |
346 | 0 | case SC_AC_UNKNOWN: |
347 | 0 | return "N/A"; |
348 | 0 | case SC_AC_NEVER: |
349 | 0 | return "NEVR"; |
350 | 0 | case SC_AC_NONE: |
351 | 0 | return "NONE"; |
352 | 0 | case SC_AC_CHV: |
353 | 0 | strcpy(buf, "CHV"); |
354 | 0 | if (e->key_ref != SC_AC_KEY_REF_NONE) |
355 | 0 | sprintf(buf + 3, "%d", e->key_ref); |
356 | 0 | break; |
357 | 0 | case SC_AC_TERM: |
358 | 0 | strcpy(buf, "TERM"); |
359 | 0 | break; |
360 | 0 | case SC_AC_PRO: |
361 | 0 | strcpy(buf, "PROT"); |
362 | 0 | break; |
363 | 0 | case SC_AC_AUT: |
364 | 0 | strcpy(buf, "AUTH"); |
365 | 0 | if (e->key_ref != SC_AC_KEY_REF_NONE) |
366 | 0 | sprintf(buf + 4, "%d", e->key_ref); |
367 | 0 | break; |
368 | 0 | case SC_AC_SEN: |
369 | 0 | strcpy(buf, "Sec.Env. "); |
370 | 0 | if (e->key_ref != SC_AC_KEY_REF_NONE) |
371 | 0 | sprintf(buf + 3, "#%d", e->key_ref); |
372 | 0 | break; |
373 | 0 | case SC_AC_SCB: |
374 | 0 | strcpy(buf, "Sec.ControlByte "); |
375 | 0 | if (e->key_ref != SC_AC_KEY_REF_NONE) |
376 | 0 | sprintf(buf + 3, "Ox%X", e->key_ref); |
377 | 0 | break; |
378 | 0 | case SC_AC_IDA: |
379 | 0 | strcpy(buf, "PKCS#15 AuthID "); |
380 | 0 | if (e->key_ref != SC_AC_KEY_REF_NONE) |
381 | 0 | sprintf(buf + 3, "#%d", e->key_ref); |
382 | 0 | break; |
383 | 0 | default: |
384 | 0 | strcpy(buf, "????"); |
385 | 0 | break; |
386 | 0 | } |
387 | 0 | strlcat(line, buf, sizeof line); |
388 | 0 | strlcat(line, " ", sizeof line); |
389 | 0 | e = e->next; |
390 | 0 | } |
391 | 0 | line[(sizeof line)-1] = '\0'; /* make sure it's NUL terminated */ |
392 | 0 | line[strlen(line)-1] = 0; /* get rid of trailing space */ |
393 | 0 | return line; |
394 | 0 | } |
395 | | |
396 | | NORETURN void |
397 | | util_fatal(const char *fmt, ...) |
398 | 0 | { |
399 | 0 | va_list ap; |
400 | |
|
401 | 0 | va_start(ap, fmt); |
402 | 0 | fprintf(stderr, "error: "); |
403 | 0 | vfprintf(stderr, fmt, ap); |
404 | 0 | fprintf(stderr, "\nAborting.\n"); |
405 | 0 | va_end(ap); |
406 | |
|
407 | 0 | sc_notify_close(); |
408 | |
|
409 | 0 | exit(1); |
410 | 0 | } |
411 | | |
412 | | void |
413 | | util_error(const char *fmt, ...) |
414 | 0 | { |
415 | 0 | va_list ap; |
416 | |
|
417 | 0 | va_start(ap, fmt); |
418 | 0 | fprintf(stderr, "error: "); |
419 | 0 | vfprintf(stderr, fmt, ap); |
420 | 0 | fprintf(stderr, "\n"); |
421 | 0 | va_end(ap); |
422 | 0 | } |
423 | | |
424 | | void |
425 | | util_warn(const char *fmt, ...) |
426 | 0 | { |
427 | 0 | va_list ap; |
428 | |
|
429 | 0 | va_start(ap, fmt); |
430 | 0 | fprintf(stderr, "warning: "); |
431 | 0 | vfprintf(stderr, fmt, ap); |
432 | 0 | fprintf(stderr, "\n"); |
433 | 0 | va_end(ap); |
434 | 0 | } |
435 | | |
436 | | int |
437 | | util_getpass (char **lineptr, size_t *len, FILE *stream) |
438 | 0 | { |
439 | 0 | #define MAX_PASS_SIZE 128 |
440 | 0 | char *buf; |
441 | 0 | size_t i; |
442 | 0 | int ch = 0; |
443 | 0 | #ifndef _WIN32 |
444 | 0 | struct termios old, new; |
445 | |
|
446 | 0 | fflush(stdout); |
447 | 0 | if (tcgetattr (fileno (stdout), &old) != 0) |
448 | 0 | return -1; |
449 | 0 | new = old; |
450 | 0 | new.c_lflag &= ~ECHO; |
451 | 0 | if (tcsetattr (fileno (stdout), TCSAFLUSH, &new) != 0) |
452 | 0 | return -1; |
453 | 0 | #endif |
454 | | |
455 | 0 | buf = calloc(1, MAX_PASS_SIZE); |
456 | 0 | if (!buf) |
457 | 0 | return -1; |
458 | | |
459 | 0 | for (i = 0; i < MAX_PASS_SIZE - 1; i++) { |
460 | 0 | #ifndef _WIN32 |
461 | 0 | ch = getchar(); |
462 | | #else |
463 | | ch = _getch(); |
464 | | #endif |
465 | 0 | if (ch == 0 || ch == 3) |
466 | 0 | break; |
467 | 0 | if (ch == '\n' || ch == '\r') |
468 | 0 | break; |
469 | | |
470 | 0 | buf[i] = (char) ch; |
471 | 0 | } |
472 | 0 | #ifndef _WIN32 |
473 | 0 | tcsetattr (fileno (stdout), TCSAFLUSH, &old); |
474 | 0 | fputs("\n", stdout); |
475 | 0 | #endif |
476 | 0 | if (ch == 0 || ch == 3) { |
477 | 0 | free(buf); |
478 | 0 | return -1; |
479 | 0 | } |
480 | | |
481 | 0 | if (*lineptr && (!len || *len < i+1)) { |
482 | 0 | free(*lineptr); |
483 | 0 | *lineptr = NULL; |
484 | 0 | } |
485 | |
|
486 | 0 | if (*lineptr) { |
487 | 0 | memcpy(*lineptr,buf,i+1); |
488 | 0 | memset(buf, 0, MAX_PASS_SIZE); |
489 | 0 | free(buf); |
490 | 0 | } else { |
491 | 0 | *lineptr = buf; |
492 | 0 | if (len) |
493 | 0 | *len = MAX_PASS_SIZE; |
494 | 0 | } |
495 | 0 | return (int)i; |
496 | 0 | } |
497 | | |
498 | | size_t |
499 | | util_get_pin(const char *input, const char **pin) |
500 | 0 | { |
501 | 0 | size_t inputlen; |
502 | 0 | size_t pinlen = 0; |
503 | |
|
504 | 0 | if (!input || !pin) { |
505 | 0 | return 0; |
506 | 0 | } |
507 | 0 | inputlen = strlen(input); |
508 | |
|
509 | 0 | if (inputlen > 4 && strncasecmp(input, "env:", 4) == 0) { |
510 | | // Get a PIN from a environment variable |
511 | 0 | *pin = getenv(input + 4); |
512 | 0 | pinlen = *pin ? strlen(*pin) : 0; |
513 | 0 | } else if (inputlen > 5 && strncasecmp(input, "file:", 5) == 0) { |
514 | 0 | fprintf(stderr, "Reading PIN from file not supported!\n"); |
515 | 0 | } else { |
516 | | //Just use the input |
517 | 0 | *pin = input; |
518 | 0 | pinlen = inputlen; |
519 | 0 | } |
520 | 0 | return pinlen; |
521 | 0 | } |