1 /* ykclient.c --- Implementation of Yubikey OTP validation client library.
2 *
3 * Written by Simon Josefsson <simon@josefsson.org>.
4 * Copyright (c) 2006-2013 Yubico AB
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
33 #include "ykclient.h"
34
35 #include "ykclient_server_response.h"
36
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <stdarg.h>
40 #include <ctype.h>
41
42 #include <curl/curl.h>
43
44 #include "sha.h"
45 #include "cencode.h"
46 #include "cdecode.h"
47
48 #define NONCE_LEN 32
49 #define MAX_TEMPLATES 255
50
51 #define ADD_NONCE "&nonce="
52 #define ADD_OTP "&otp="
53 #define ADD_HASH "&h="
54 #define ADD_ID "?id="
55 #define ADD_TS "×tamp=1"
56
57 #define TEMPLATE_FORMAT_OLD 1
58 #define TEMPLATE_FORMAT_NEW 2
59
60 struct ykclient_st
61 {
62 const char *ca_path;
63 const char *ca_info;
64 const char *proxy;
65 size_t num_templates;
66 char **url_templates;
67 int template_format;
68 char last_url[256];
69 unsigned int client_id;
70 size_t keylen;
71 const char *key;
72 char *key_buf;
73 char *nonce;
74 char nonce_supplied;
75 int verify_signature;
76 ykclient_server_response_t *srv_response;
77 };
78
79 struct curl_data
80 {
81 char *curl_chunk;
82 size_t curl_chunk_size;
83 };
84
85 struct ykclient_handle_st
86 {
87 CURL **easy;
88 CURLM *multi;
89 size_t num_easy;
90 struct curl_data *data;
91 char **url_exp;
92 };
93
94 static const char *default_url_templates[] = {
95 "https://api.yubico.com/wsapi/2.0/verify",
96 "https://api2.yubico.com/wsapi/2.0/verify",
97 "https://api3.yubico.com/wsapi/2.0/verify",
98 "https://api4.yubico.com/wsapi/2.0/verify",
99 "https://api5.yubico.com/wsapi/2.0/verify",
100 };
101
102 static const char *user_agent = PACKAGE "/" PACKAGE_VERSION;
103
104 /** Initialise the global context for the library
105 *
106 * This function is not thread safe. It must be invoked successfully
107 * before using any other function.
108 *
109 * @return one of the YKCLIENT_* values or YKCLIENT_OK on success.
110 */
111 ykclient_rc
ykclient_global_init(void)112 ykclient_global_init (void)
113 {
114
115 if (curl_global_init (CURL_GLOBAL_ALL) != 0)
116 return YKCLIENT_CURL_INIT_ERROR;
117 return YKCLIENT_OK;
118 }
119
120 /** Deinitialise the global context for the library
121 *
122 * This function is not thread safe. After this function has been
123 * called, no other library functions may be used reliably.
124 *
125 * @return one of the YKCLIENT_* values or YKCLIENT_OK on success.
126 */
127 void
ykclient_global_done(void)128 ykclient_global_done (void)
129 {
130 curl_global_cleanup ();
131 }
132
133 /** Initialise a new configuration structure
134 *
135 * Additional options can be set with ykclient_set_* functions
136 * after memory for the configuration has been allocated with
137 * this function.
138 *
139 * @param ykc Where to write a pointer to the new configuration.
140 * @return one of the YKCLIENT_* values or YKCLIENT_OK on success.
141 */
142 ykclient_rc
ykclient_init(ykclient_t ** ykc)143 ykclient_init (ykclient_t ** ykc)
144 {
145 ykclient_t *p;
146
147 p = malloc (sizeof (*p));
148
149 if (!p)
150 {
151 return YKCLIENT_OUT_OF_MEMORY;
152 }
153
154 memset (p, 0, (sizeof (*p)));
155
156 p->ca_path = NULL;
157 p->ca_info = NULL;
158 p->proxy = NULL;
159
160 p->key = NULL;
161 p->keylen = 0;
162 p->key_buf = NULL;
163
164 memset (p->last_url, 0, sizeof (p->last_url));
165
166 p->nonce = NULL;
167 p->nonce_supplied = 0;
168
169 p->srv_response = NULL;
170
171 /*
172 * Verification of server signature can only be done if there is
173 * an API key provided
174 */
175 p->verify_signature = 0;
176
177 /*
178 * Set the default URLs (these can be overridden later)
179 */
180 ykclient_set_url_bases (p,
181 sizeof (default_url_templates) /
182 sizeof (char *), default_url_templates);
183
184 *ykc = p;
185
186 return YKCLIENT_OK;
187 }
188
189 /** Frees a configuration structure allocated by ykclient_init
190 *
191 * Any handles created with ykclient_handle_init must be freed
192 * separately with ykclient_handle_done.
193 *
194 * @param ykc configuration to free.
195 */
196 void
ykclient_done(ykclient_t ** ykc)197 ykclient_done (ykclient_t ** ykc)
198 {
199 if (ykc && *ykc)
200 {
201 if ((*ykc)->url_templates)
202 {
203 size_t i;
204 for (i = 0; i < (*ykc)->num_templates; i++)
205 {
206 free ((*ykc)->url_templates[i]);
207 }
208 free ((*ykc)->url_templates);
209 }
210
211 if ((*ykc)->srv_response)
212 {
213 ykclient_server_response_free((*ykc)->srv_response);
214 }
215
216 free ((*ykc)->key_buf);
217 free (*ykc);
218 }
219
220 if (ykc)
221 {
222 *ykc = NULL;
223 }
224 }
225
226 /** Callback for processing CURL data received from the validation server
227 *
228 */
229 static size_t
curl_callback(void * ptr,size_t size,size_t nmemb,void * data)230 curl_callback (void *ptr, size_t size, size_t nmemb, void *data)
231 {
232 struct curl_data *curl_data = (struct curl_data *) data;
233 size_t realsize = size * nmemb;
234 char *p;
235
236 if (curl_data->curl_chunk)
237 {
238 p = realloc (curl_data->curl_chunk,
239 curl_data->curl_chunk_size + realsize + 1);
240 }
241 else
242 {
243 p = malloc (curl_data->curl_chunk_size + realsize + 1);
244 }
245
246 if (!p)
247 {
248 return 0;
249 }
250
251 curl_data->curl_chunk = p;
252
253 memcpy (&(curl_data->curl_chunk[curl_data->curl_chunk_size]), ptr,
254 realsize);
255 curl_data->curl_chunk_size += realsize;
256 curl_data->curl_chunk[curl_data->curl_chunk_size] = 0;
257
258 return realsize;
259 }
260
261 /** Create a new handle
262 *
263 * These handles contain curl easy and multi handles required to
264 * process a request.
265 *
266 * This must be called after configuring template URLs, and handles
267 * MUST NOT be reused if new template URLs have been set.
268 *
269 * If new template URLs have been set all handles must be destroyed
270 * with ykclient_handle_close and recreated with this function.
271 *
272 * Handles must be cleaned with ykclient_handle_cleanup between
273 * requests, and closed with ykclient_handle_close when they are no
274 * longer needed.
275 *
276 * @param ykc Yubikey client configuration.
277 * @param[out] ykh where to write pointer to new handle.
278 * @return one of the YKCLIENT_* values or YKCLIENT_OK on success.
279 */
280 ykclient_rc
ykclient_handle_init(ykclient_t * ykc,ykclient_handle_t ** ykh)281 ykclient_handle_init (ykclient_t * ykc, ykclient_handle_t ** ykh)
282 {
283 ykclient_handle_t *p;
284
285 *ykh = NULL;
286
287 p = malloc (sizeof (*p));
288 if (!p)
289 {
290 return YKCLIENT_OUT_OF_MEMORY;
291 }
292 memset (p, 0, (sizeof (*p)));
293
294 p->multi = curl_multi_init ();
295 if (!p->multi)
296 {
297 free (p);
298 return YKCLIENT_CURL_INIT_ERROR;
299 }
300
301 p->easy = malloc (sizeof (CURL *) * ykc->num_templates);
302 for (p->num_easy = 0; p->num_easy < ykc->num_templates; p->num_easy++)
303 {
304 CURL *easy;
305 struct curl_data *data;
306
307 data = malloc (sizeof (*data));
308 if (!data)
309 {
310 ykclient_handle_done (&p);
311 return YKCLIENT_OUT_OF_MEMORY;
312 }
313 data->curl_chunk = NULL;
314 data->curl_chunk_size = 0;
315
316 easy = curl_easy_init ();
317 if (!easy)
318 {
319 free (data);
320 ykclient_handle_done (&p);
321 return YKCLIENT_CURL_INIT_ERROR;
322 }
323
324 if (ykc->ca_path)
325 {
326 curl_easy_setopt (easy, CURLOPT_CAPATH, ykc->ca_path);
327 }
328
329 if (ykc->ca_info)
330 {
331 curl_easy_setopt (easy, CURLOPT_CAINFO, ykc->ca_info);
332 }
333
334 if (ykc->proxy)
335 {
336 /*
337 * The proxy string may be prefixed with [scheme]://ip:port to specify which kind of proxy is used.
338 * Valid choices are: socks4://, socks4a://, socks5:// or socks5h://
339 * Use socks5h to ask the proxy to do the dns resolving.
340 * If no scheme or port is specified HTTP proxy port 1080 will be used.
341 */
342 curl_easy_setopt (easy, CURLOPT_PROXY, ykc->proxy);
343 }
344
345 curl_easy_setopt (easy, CURLOPT_WRITEDATA, (void *) data);
346 curl_easy_setopt (easy, CURLOPT_PRIVATE, (void *) data);
347 curl_easy_setopt (easy, CURLOPT_WRITEFUNCTION, curl_callback);
348 curl_easy_setopt (easy, CURLOPT_USERAGENT, user_agent);
349
350 curl_multi_add_handle (p->multi, easy);
351 p->easy[p->num_easy] = easy;
352 }
353
354 if(p->num_easy == 0) {
355 ykclient_handle_done (&p);
356 return YKCLIENT_BAD_INPUT;
357 }
358
359 /* Take this opportunity to allocate the array for expanded URLs */
360 p->url_exp = malloc (sizeof (char *) * p->num_easy);
361 if (!p->url_exp)
362 {
363 ykclient_handle_done (&p);
364 return YKCLIENT_OUT_OF_MEMORY;
365 }
366 memset (p->url_exp, 0, (sizeof (char *) * p->num_easy));
367
368 *ykh = p;
369
370 return YKCLIENT_OK;
371 }
372
373 /** Cleanup memory allocated for requests
374 *
375 * Cleans up any memory allocated and held by the handle for a
376 * request. Must be called after each request.
377 *
378 * @param ykh to close.
379 */
380 void
ykclient_handle_cleanup(ykclient_handle_t * ykh)381 ykclient_handle_cleanup (ykclient_handle_t * ykh)
382 {
383 size_t i;
384 struct curl_data *data;
385 int requests = 0;
386
387 /*
388 * Curl will not allow a connection to be re-used unless the
389 * request finished, call curl_multi_perform one last time
390 * to give libcurl an opportunity to mark the request as
391 * complete.
392 *
393 * If the delay between yk_request_send and
394 * ykclient_handle_cleanup is sufficient to allow the request
395 * to complete, the connection can be re-used, else it will
396 * be re-established on next yk_request_send.
397 */
398 (void) curl_multi_perform (ykh->multi, &requests);
399
400 for (i = 0; i < ykh->num_easy; i++)
401 {
402 free (ykh->url_exp[i]);
403 ykh->url_exp[i] = NULL;
404
405 curl_easy_getinfo (ykh->easy[i], CURLINFO_PRIVATE, (char **) &data);
406 free (data->curl_chunk);
407 data->curl_chunk = NULL;
408 data->curl_chunk_size = 0;
409
410 curl_multi_remove_handle (ykh->multi, ykh->easy[i]);
411 curl_multi_add_handle (ykh->multi, ykh->easy[i]);
412 }
413 }
414
415 /** Close a handle freeing memory and destroying connections
416 *
417 * Frees any memory allocated for the handle, and calls various CURL
418 * functions to destroy all curl easy and multi handles created for
419 * this handle.
420 *
421 * @param ykh to close.
422 */
423 void
ykclient_handle_done(ykclient_handle_t ** ykh)424 ykclient_handle_done (ykclient_handle_t ** ykh)
425 {
426 struct curl_data *data;
427 size_t i;
428
429 if (ykh && *ykh)
430 {
431 ykclient_handle_cleanup (*ykh);
432
433 for (i = 0; i < (*ykh)->num_easy; i++)
434 {
435 curl_easy_getinfo ((*ykh)->easy[i], CURLINFO_PRIVATE,
436 (char **) &data);
437 free (data);
438
439 curl_multi_remove_handle ((*ykh)->multi, (*ykh)->easy[i]);
440 curl_easy_cleanup ((*ykh)->easy[i]);
441 }
442
443 if ((*ykh)->multi)
444 {
445 curl_multi_cleanup ((*ykh)->multi);
446 }
447
448 free ((*ykh)->url_exp);
449 free ((*ykh)->easy);
450 free (*ykh);
451 }
452
453 if (ykh)
454 {
455 *ykh = NULL;
456 }
457 }
458
459 void
ykclient_set_verify_signature(ykclient_t * ykc,int value)460 ykclient_set_verify_signature (ykclient_t * ykc, int value)
461 {
462 if (ykc == NULL)
463 {
464 return;
465 }
466
467 ykc->verify_signature = value;
468 }
469
470 void
ykclient_set_client(ykclient_t * ykc,unsigned int client_id,size_t keylen,const char * key)471 ykclient_set_client (ykclient_t * ykc,
472 unsigned int client_id, size_t keylen, const char *key)
473 {
474 ykc->client_id = client_id;
475 ykc->keylen = keylen;
476 ykc->key = key;
477 }
478
479 ykclient_rc
ykclient_set_client_hex(ykclient_t * ykc,unsigned int client_id,const char * key)480 ykclient_set_client_hex (ykclient_t * ykc,
481 unsigned int client_id, const char *key)
482 {
483 size_t i;
484 size_t key_len;
485 size_t bin_len;
486
487 ykc->client_id = client_id;
488
489 if (key == NULL)
490 {
491 return YKCLIENT_OK;
492 }
493
494 key_len = strlen (key);
495
496 if (key_len % 2 != 0)
497 {
498 return YKCLIENT_HEX_DECODE_ERROR;
499 }
500
501 bin_len = key_len / 2;
502
503 free (ykc->key_buf);
504
505 ykc->key_buf = malloc (bin_len);
506 if (!ykc->key_buf)
507 {
508 return YKCLIENT_OUT_OF_MEMORY;
509 }
510
511 for (i = 0; i < bin_len; i++)
512 {
513 int tmp;
514
515 if (sscanf (&key[2 * i], "%02x", &tmp) != 1)
516 {
517 free (ykc->key_buf);
518 ykc->key_buf = NULL;
519 return YKCLIENT_HEX_DECODE_ERROR;
520 }
521
522 ykc->key_buf[i] = tmp;
523 }
524
525 ykc->keylen = bin_len;
526 ykc->key = ykc->key_buf;
527
528 return YKCLIENT_OK;
529 }
530
531 ykclient_rc
ykclient_set_client_b64(ykclient_t * ykc,unsigned int client_id,const char * key)532 ykclient_set_client_b64 (ykclient_t * ykc,
533 unsigned int client_id, const char *key)
534 {
535 size_t key_len;
536 ssize_t dec_len;
537
538 base64_decodestate b64;
539
540 ykc->client_id = client_id;
541
542 if (key == NULL)
543 {
544 return YKCLIENT_OK;
545 }
546
547 key_len = strlen (key);
548
549 free (ykc->key_buf);
550
551 ykc->key_buf = malloc (key_len + 1);
552 if (!ykc->key_buf)
553 {
554 return YKCLIENT_OUT_OF_MEMORY;
555 }
556
557 base64_init_decodestate (&b64);
558 dec_len = (ssize_t) base64_decode_block (key, key_len, ykc->key_buf, &b64);
559 if (dec_len < 0)
560 {
561 return YKCLIENT_BASE64_DECODE_ERROR;
562 }
563 ykc->keylen = (size_t) dec_len;
564 ykc->key = ykc->key_buf;
565
566 /* Now that we have a key the sensible default is to verify signatures */
567 ykc->verify_signature = 1;
568
569 return YKCLIENT_OK;
570 }
571
572 /** Set the CA path
573 *
574 * Must be called before creating handles.
575 */
576 void
ykclient_set_ca_path(ykclient_t * ykc,const char * ca_path)577 ykclient_set_ca_path (ykclient_t * ykc, const char *ca_path)
578 {
579 ykc->ca_path = ca_path;
580 }
581
582 /** Set the CA info, needed for linking with GnuTLS
583 *
584 * Must be called before creating handles.
585 */
586 void
ykclient_set_ca_info(ykclient_t * ykc,const char * ca_info)587 ykclient_set_ca_info (ykclient_t * ykc, const char *ca_info)
588 {
589 ykc->ca_info = ca_info;
590 }
591
592 /** Set the proxy
593 *
594 * Must be called before creating handles.
595 */
596 void
ykclient_set_proxy(ykclient_t * ykc,const char * proxy)597 ykclient_set_proxy (ykclient_t * ykc, const char *proxy)
598 {
599 ykc->proxy = proxy;
600 }
601
602
603 /** Set a single URL template
604 *
605 * @param ykc Yubikey client configuration.
606 * @param url_template to set.
607 * @return one of the YKCLIENT_* values or YKCLIENT_OK on success.
608 */
609 ykclient_rc
ykclient_set_url_template(ykclient_t * ykc,const char * url_template)610 ykclient_set_url_template (ykclient_t * ykc, const char *url_template)
611 {
612 return ykclient_set_url_templates (ykc, 1, (const char **) &url_template);
613 }
614
615 /** Set the URLs of the YK validation servers
616 *
617 * The URL strings will be copied to the new buffers, so the
618 * caller may free the original URL strings if they are no
619 * longer needed.
620 *
621 * @note This function MUST be called before calling ykclient_handle_init
622 *
623 * @param ykc Yubikey client configuration.
624 * @param num_templates Number of template URLs passed in url_templates.
625 * @param url_templates Array of template URL strings.
626 * @return one of the YKCLIENT_* values or YKCLIENT_OK on success.
627 */
628 ykclient_rc
ykclient_set_url_templates(ykclient_t * ykc,size_t num_templates,const char ** url_templates)629 ykclient_set_url_templates (ykclient_t * ykc, size_t num_templates,
630 const char **url_templates)
631 {
632 ykclient_rc ret =
633 ykclient_set_url_bases (ykc, num_templates, url_templates);
634 if (ret == YKCLIENT_OK)
635 {
636 ykc->template_format = TEMPLATE_FORMAT_OLD;
637 }
638 return ret;
639 }
640
641 ykclient_rc
ykclient_set_url_bases(ykclient_t * ykc,size_t num_templates,const char ** url_templates)642 ykclient_set_url_bases (ykclient_t * ykc, size_t num_templates,
643 const char **url_templates)
644 {
645 size_t i;
646 if (num_templates > MAX_TEMPLATES)
647 {
648 return YKCLIENT_BAD_INPUT;
649 }
650
651 /* Clean out any previously allocated templates */
652 if (ykc->url_templates)
653 {
654 for (i = 0; i < ykc->num_templates; i++)
655 {
656 free (ykc->url_templates[i]);
657 }
658 free (ykc->url_templates);
659 }
660
661 /* Reallocate the template array */
662 ykc->url_templates = malloc (sizeof (char *) * num_templates);
663 if (!ykc->url_templates)
664 {
665 return YKCLIENT_OUT_OF_MEMORY;
666 }
667 memset (ykc->url_templates, 0, (sizeof (char *) * num_templates));
668
669 for (ykc->num_templates = 0; ykc->num_templates < num_templates;
670 ykc->num_templates++)
671 {
672 ykc->url_templates[ykc->num_templates] =
673 strdup (url_templates[ykc->num_templates]);
674
675 if (!ykc->url_templates[ykc->num_templates])
676 {
677 return YKCLIENT_OUT_OF_MEMORY;
678 }
679 }
680
681 ykc->template_format = TEMPLATE_FORMAT_NEW;
682 return YKCLIENT_OK;
683 }
684
685 /*
686 * Set the nonce. A default nonce is generated in ykclient_init (), but
687 * if you either want to specify your own nonce, or want to remove the
688 * nonce (needed to send signed requests to v1 validation servers),
689 * you must call this function. Set nonce to NULL to disable it.
690 */
691 void
ykclient_set_nonce(ykclient_t * ykc,char * nonce)692 ykclient_set_nonce (ykclient_t * ykc, char *nonce)
693 {
694 ykc->nonce_supplied = 1;
695 ykc->nonce = nonce;
696 }
697
698 /** Convert a ykclient_rc value to a string
699 *
700 * Returns a more verbose error message relating to the ykclient_rc
701 * value passed as ret.
702 *
703 * @param ret the error code to convert.
704 * @return verbose error string.
705 */
706 const char *
ykclient_strerror(ykclient_rc ret)707 ykclient_strerror (ykclient_rc ret)
708 {
709 const char *p;
710
711 switch (ret)
712 {
713 case YKCLIENT_OK:
714 p = "Success";
715 break;
716
717 case YKCLIENT_CURL_PERFORM_ERROR:
718 p = "Error performing curl";
719 break;
720
721 case YKCLIENT_BAD_OTP:
722 p = "Yubikey OTP was bad (BAD_OTP)";
723 break;
724
725 case YKCLIENT_REPLAYED_OTP:
726 p = "Yubikey OTP was replayed (REPLAYED_OTP)";
727 break;
728
729 case YKCLIENT_REPLAYED_REQUEST:
730 p = "Yubikey request was replayed (REPLAYED_REQUEST)";
731 break;
732
733 case YKCLIENT_BAD_SIGNATURE:
734 p = "Request signature was invalid (BAD_SIGNATURE)";
735 break;
736
737 case YKCLIENT_BAD_SERVER_SIGNATURE:
738 p = "Server response signature was invalid (BAD_SERVER_SIGNATURE)";
739 break;
740
741 case YKCLIENT_MISSING_PARAMETER:
742 p = "Request was missing a parameter (MISSING_PARAMETER)";
743 break;
744
745 case YKCLIENT_NO_SUCH_CLIENT:
746 p = "Client identity does not exist (NO_SUCH_CLIENT)";
747 break;
748
749 case YKCLIENT_OPERATION_NOT_ALLOWED:
750 p = "Authorization denied (OPERATION_NOT_ALLOWED)";
751 break;
752
753 case YKCLIENT_BACKEND_ERROR:
754 p = "Internal server error (BACKEND_ERROR)";
755 break;
756
757 case YKCLIENT_NOT_ENOUGH_ANSWERS:
758 p = "Too few validation servers available (NOT_ENOUGH_ANSWERS)";
759 break;
760
761 case YKCLIENT_OUT_OF_MEMORY:
762 p = "Out of memory";
763 break;
764
765 case YKCLIENT_PARSE_ERROR:
766 p = "Could not parse server response";
767 break;
768
769 case YKCLIENT_FORMAT_ERROR:
770 p = "Internal printf format error";
771 break;
772
773 case YKCLIENT_CURL_INIT_ERROR:
774 p = "Error initializing curl";
775 break;
776
777 case YKCLIENT_HMAC_ERROR:
778 p = "HMAC signature validation/generation error";
779 break;
780
781 case YKCLIENT_HEX_DECODE_ERROR:
782 p = "Error decoding hex string";
783 break;
784
785 case YKCLIENT_BASE64_DECODE_ERROR:
786 p = "Error decoding base64 string";
787 break;
788
789 case YKCLIENT_NOT_IMPLEMENTED:
790 p = "Not implemented";
791 break;
792
793 case YKCLIENT_HANDLE_NOT_REINIT:
794 p = "Request template URLs modified without reinitialising handles";
795 break;
796
797 case YKCLIENT_BAD_INPUT:
798 p = "Passed invalid or incorrect number of parameters";
799 break;
800
801 default:
802 p = "Unknown error";
803 }
804
805 return p;
806 }
807
808 /** Generates or duplicates an existing nonce value
809 *
810 * If a nonce value was set with ykclient_set_nonce, it will be duplicated
811 * and a pointer to the memory returned in nonce.
812 *
813 * If a nonce value has not been set a new buffer will be allocated and a
814 * random string of NONCE_LEN will be written to it.
815 *
816 * Memory pointed to by nonce must be freed by the called when it is no
817 * longer requiest.
818 *
819 * @param ykc Yubikey client configuration.
820 * @param[out] nonce where to write the pointer to the nonce value.
821 * @return one of the YKCLIENT_* values or YKCLIENT_OK on success.
822 */
823 static ykclient_rc
ykclient_generate_nonce(ykclient_t * ykc,char ** nonce)824 ykclient_generate_nonce (ykclient_t * ykc, char **nonce)
825 {
826 *nonce = NULL;
827
828 /*
829 * If we were supplied with a static value just strdup,
830 * makes memory management easier.
831 */
832 if (ykc->nonce_supplied)
833 {
834 if (ykc->nonce)
835 {
836 *nonce = strdup (ykc->nonce);
837 if (*nonce == NULL)
838 return YKCLIENT_OUT_OF_MEMORY;
839 }
840 }
841 /*
842 * Generate a random 'nonce' value
843 */
844 else
845 {
846 struct timeval tv;
847 size_t i;
848 char *p;
849
850 p = malloc (NONCE_LEN + 1);
851 if (!p)
852 {
853 return YKCLIENT_OUT_OF_MEMORY;
854 }
855
856 gettimeofday (&tv, 0);
857 srandom (tv.tv_sec * tv.tv_usec);
858
859 for (i = 0; i < NONCE_LEN; ++i)
860 {
861 p[i] = (random () % 26) + 'a';
862 }
863
864 p[NONCE_LEN] = 0;
865
866 *nonce = p;
867 }
868
869 return YKCLIENT_OK;
870 }
871
872 static ykclient_rc
ykclient_expand_new_url(const char * template,const char * encoded_otp,const char * nonce,int client_id,char ** url_exp)873 ykclient_expand_new_url (const char *template,
874 const char *encoded_otp, const char *nonce,
875 int client_id, char **url_exp)
876 {
877 size_t len =
878 strlen (template) + strlen (encoded_otp) + strlen (ADD_OTP) +
879 strlen (ADD_ID) + strlen(ADD_TS) + 1;
880 len += snprintf (NULL, 0, "%d", client_id);
881
882 if (nonce)
883 {
884 len += strlen (nonce) + strlen (ADD_NONCE);
885 }
886
887 *url_exp = malloc (len);
888 if (!*url_exp)
889 {
890 return YKCLIENT_OUT_OF_MEMORY;
891 }
892
893 if (nonce)
894 {
895 snprintf (*url_exp, len, "%s" ADD_ID "%d" ADD_NONCE "%s" ADD_OTP "%s" ADD_TS,
896 template, client_id, nonce, encoded_otp);
897 }
898 else
899 {
900 snprintf (*url_exp, len, "%s" ADD_ID "%d" ADD_OTP "%s" ADD_TS, template,
901 client_id, encoded_otp);
902 }
903 return YKCLIENT_OK;
904 }
905
906 static ykclient_rc
ykclient_expand_old_url(const char * template,const char * encoded_otp,const char * nonce,int client_id,char ** url_exp)907 ykclient_expand_old_url (const char *template,
908 const char *encoded_otp, const char *nonce,
909 int client_id, char **url_exp)
910 {
911 {
912 size_t len;
913 ssize_t wrote;
914
915 len = strlen (template) + strlen (encoded_otp) + 20;
916 *url_exp = malloc (len);
917 if (!*url_exp)
918 {
919 return YKCLIENT_OUT_OF_MEMORY;
920 }
921
922 wrote = snprintf (*url_exp, len, template, client_id, encoded_otp);
923 if (wrote < 0 || (size_t) wrote > len)
924 {
925 return YKCLIENT_FORMAT_ERROR;
926 }
927 }
928
929 if (nonce)
930 {
931 /* Create new URL with nonce in it. */
932 char *nonce_url, *otp_offset;
933 size_t len;
934 ssize_t wrote;
935
936 len = strlen (*url_exp) + strlen (ADD_NONCE) + strlen (nonce) + 1;
937 nonce_url = malloc (len + 4); /* avoid valgrind complaint */
938 if (!nonce_url)
939 {
940 return YKCLIENT_OUT_OF_MEMORY;
941 }
942
943 /* Find the &otp= in url and insert ?nonce= before otp. Must get
944 * sorted headers since we calculate HMAC on the result.
945 *
946 * XXX this will break if the validation protocol gets a parameter that
947 * sorts in between "nonce" and "otp", because the headers we sign won't
948 * be alphabetically sorted if we insert the nonce between "nz" and "otp".
949 * Also, we assume that everyone will have at least one parameter ("id=")
950 * before "otp" so there is no need to search for "?otp=".
951 */
952 otp_offset = strstr (*url_exp, ADD_OTP);
953 if (otp_offset == NULL)
954 {
955 /* point at \0 at end of url in case there is no otp */
956 otp_offset = *url_exp + len;
957 }
958
959 /* break up ykc->url where we want to insert nonce */
960 *otp_offset = 0;
961
962 wrote = snprintf (nonce_url, len, "%s" ADD_NONCE "%s&%s", *url_exp,
963 nonce, otp_offset + 1);
964 if (wrote < 0 || (size_t) wrote + 1 != len)
965 {
966 free (nonce_url);
967 return YKCLIENT_FORMAT_ERROR;
968 }
969
970 free (*url_exp);
971 *url_exp = nonce_url;
972 }
973 return YKCLIENT_OK;
974 }
975
976 /** Expand URL templates specified with set_url_templates
977 *
978 * Expands placeholderss or inserts additional parameters for nonce,
979 * OTP, and signing values into duplicates of URL templates.
980 *
981 * The memory allocated for these duplicates must be freed
982 * by calling either ykclient_handle_done or ykclient_handle_cleanup
983 * after they are no longer needed.
984 *
985 * @param ykc Yubikey client configuration.
986 * @param ykh Yubikey client handle.
987 * @param yubikey OTP string passed to the client.
988 * @param nonce Random value included in the request and validated in the response.
989 * @return one of the YKCLIENT_* values or YKCLIENT_OK on success.
990 */
991 static ykclient_rc
ykclient_expand_urls(ykclient_t * ykc,ykclient_handle_t * ykh,const char * yubikey,const char * nonce)992 ykclient_expand_urls (ykclient_t * ykc, ykclient_handle_t * ykh,
993 const char *yubikey, const char *nonce)
994 {
995 ykclient_rc out = YKCLIENT_OK;
996
997 size_t i, j;
998
999 char *signature = NULL;
1000 char *encoded_otp = NULL;
1001
1002 /* The handle must have the same number of easy handles as we have templates */
1003 if (ykc->num_templates != ykh->num_easy)
1004 {
1005 return YKCLIENT_HANDLE_NOT_REINIT;
1006 }
1007
1008 for (i = 0; i < ykc->num_templates; i++)
1009 {
1010 ykclient_rc ret;
1011
1012 if (!encoded_otp)
1013 {
1014 /* URL-encode the OTP */
1015 encoded_otp = curl_easy_escape (ykh->easy[i], yubikey, 0);
1016 }
1017
1018 if (ykc->template_format == TEMPLATE_FORMAT_OLD)
1019 {
1020 ret = ykclient_expand_old_url (ykc->url_templates[i],
1021 encoded_otp, nonce, ykc->client_id,
1022 &ykh->url_exp[i]);
1023 }
1024 else
1025 {
1026 ret = ykclient_expand_new_url (ykc->url_templates[i],
1027 encoded_otp, nonce, ykc->client_id,
1028 &ykh->url_exp[i]);
1029 }
1030 if (ret != YKCLIENT_OK)
1031 {
1032 out = ret;
1033 goto finish;
1034 }
1035 if (ykc->key && ykc->keylen)
1036 {
1037 if (!signature)
1038 {
1039 char b64dig[3 * 4 * SHA1HashSize + 1];
1040 uint8_t digest[USHAMaxHashSize];
1041 base64_encodestate b64;
1042 char *text;
1043 int res, res2;
1044
1045 /* Find parameters to sign. */
1046 text = strchr (ykh->url_exp[i], '?');
1047 if (!text)
1048 {
1049 out = YKCLIENT_PARSE_ERROR;
1050 goto finish;
1051 }
1052 text++;
1053
1054 /* HMAC data. */
1055 res = hmac (SHA1, (unsigned char *) text, strlen (text),
1056 (const unsigned char *) ykc->key, ykc->keylen,
1057 digest);
1058 if (res != shaSuccess)
1059 {
1060 out = YKCLIENT_HMAC_ERROR;
1061 goto finish;
1062 }
1063
1064 /* Base64 signature. */
1065 base64_init_encodestate (&b64);
1066 res =
1067 base64_encode_block ((char *) digest, SHA1HashSize, b64dig,
1068 &b64);
1069 res2 = base64_encode_blockend (&b64dig[res], &b64);
1070 b64dig[res + res2 - 1] = '\0';
1071
1072 signature = curl_easy_escape (ykh->easy[i], b64dig, 0);
1073 }
1074
1075 /* Create new URL with signature ( h= ) appended to it . */
1076 {
1077 char *sign_url;
1078 size_t len;
1079 ssize_t wrote;
1080
1081 len =
1082 strlen (ykh->url_exp[i]) + strlen (ADD_HASH) +
1083 strlen (signature) + 1;
1084 sign_url = malloc (len);
1085 if (!sign_url)
1086 {
1087 free (sign_url);
1088
1089 out = YKCLIENT_OUT_OF_MEMORY;
1090 goto finish;
1091 }
1092
1093 wrote =
1094 snprintf (sign_url, len, "%s" ADD_HASH "%s", ykh->url_exp[i],
1095 signature);
1096 if (wrote < 0 || (size_t) wrote + 1 != len)
1097 {
1098 free (sign_url);
1099
1100 out = YKCLIENT_FORMAT_ERROR;
1101 goto finish;
1102 }
1103
1104 free (ykh->url_exp[i]);
1105 ykh->url_exp[i] = sign_url;
1106 }
1107 }
1108
1109 curl_easy_setopt (ykh->easy[i], CURLOPT_URL, ykh->url_exp[i]);
1110 }
1111
1112 finish:
1113
1114 if (encoded_otp)
1115 {
1116 curl_free (encoded_otp);
1117 }
1118
1119 if (signature)
1120 {
1121 curl_free (signature);
1122 }
1123
1124 /* On error free any URLs we previously built */
1125 if (out != YKCLIENT_OK)
1126 {
1127 for (j = 0; j < i; j++)
1128 {
1129 free (ykh->url_exp[j]);
1130 ykh->url_exp[j] = NULL;
1131 }
1132 }
1133
1134 return out;
1135 }
1136
1137 /** Convert the response from the validation server into a ykclient_rc
1138 *
1139 * @param status message from the validation server.
1140 * @return one of the YKCLIENT_* values.
1141 */
1142 static ykclient_rc
ykclient_parse_srv_error(const char * status)1143 ykclient_parse_srv_error (const char *status)
1144 {
1145 if (strcmp (status, "OK") == 0)
1146 {
1147 return YKCLIENT_OK;
1148 }
1149 else if (strcmp (status, "BAD_OTP") == 0)
1150 {
1151 return YKCLIENT_BAD_OTP;
1152 }
1153 else if (strcmp (status, "REPLAYED_OTP") == 0)
1154 {
1155 return YKCLIENT_REPLAYED_OTP;
1156 }
1157 else if (strcmp (status, "REPLAYED_REQUEST") == 0)
1158 {
1159 return YKCLIENT_REPLAYED_REQUEST;
1160 }
1161 else if (strcmp (status, "BAD_SIGNATURE") == 0)
1162 {
1163 return YKCLIENT_BAD_SIGNATURE;
1164 }
1165 else if (strcmp (status, "MISSING_PARAMETER") == 0)
1166 {
1167 return YKCLIENT_MISSING_PARAMETER;
1168 }
1169 else if (strcmp (status, "NO_SUCH_CLIENT") == 0)
1170 {
1171 return YKCLIENT_NO_SUCH_CLIENT;
1172 }
1173 else if (strcmp (status, "OPERATION_NOT_ALLOWED") == 0)
1174 {
1175 return YKCLIENT_OPERATION_NOT_ALLOWED;
1176 }
1177 else if (strcmp (status, "BACKEND_ERROR") == 0)
1178 {
1179 return YKCLIENT_BACKEND_ERROR;
1180 }
1181 else if (strcmp (status, "NOT_ENOUGH_ANSWERS") == 0)
1182 {
1183 return YKCLIENT_NOT_ENOUGH_ANSWERS;
1184 }
1185
1186 return YKCLIENT_PARSE_ERROR;
1187 }
1188
1189 /** Send requests to one or more validation servers and processes the response
1190 *
1191 * @param ykc Yubikey client configuration.
1192 * @param ykh Yubikey client handle.
1193 * @param yubikey OTP string passed to the client.
1194 * @param nonce Random value included in the request and validated in the response.
1195 * @return one of the YKCLIENT_* values or YKCLIENT_OK on success.
1196 */
1197 static ykclient_rc
ykclient_request_send(ykclient_t * ykc,ykclient_handle_t * ykh,const char * yubikey,char * nonce)1198 ykclient_request_send (ykclient_t * ykc, ykclient_handle_t * ykh,
1199 const char *yubikey, char *nonce)
1200 {
1201 ykclient_rc out = YKCLIENT_OK;
1202 int requests;
1203
1204 if (!ykc->num_templates)
1205 {
1206 return YKCLIENT_MISSING_PARAMETER;
1207 }
1208
1209 /* The handle must have the same number of easy handles as we have templates */
1210 if (ykc->num_templates != ykh->num_easy)
1211 {
1212 return YKCLIENT_HANDLE_NOT_REINIT;
1213 }
1214
1215 memset (ykc->last_url, 0, sizeof (ykc->last_url));
1216
1217 /* Perform the request */
1218 do
1219 {
1220 int msgs = 1;
1221 CURLMcode curl_ret = curl_multi_perform (ykh->multi, &requests);
1222 struct timeval timeout;
1223
1224 fd_set fdread;
1225 fd_set fdwrite;
1226 fd_set fdexcep;
1227 int maxfd = -1;
1228
1229 long curl_timeo = -1;
1230
1231 /* curl before 7.20.0 can return CURLM_CALL_MULTI_PERFORM, continue so we
1232 * call curl_multi_perform again. */
1233 if (curl_ret == CURLM_CALL_MULTI_PERFORM)
1234 {
1235 continue;
1236 }
1237
1238 if (curl_ret != CURLM_OK)
1239 {
1240 fprintf (stderr, "Error with curl: %s\n",
1241 curl_multi_strerror (curl_ret));
1242 out = YKCLIENT_CURL_PERFORM_ERROR;
1243 goto finish;
1244 }
1245
1246 FD_ZERO (&fdread);
1247 FD_ZERO (&fdwrite);
1248 FD_ZERO (&fdexcep);
1249
1250 memset (&timeout, 0, sizeof (timeout));
1251
1252 timeout.tv_sec = 0;
1253 timeout.tv_usec = 250000;
1254
1255 curl_multi_timeout (ykh->multi, &curl_timeo);
1256 if (curl_timeo >= 0)
1257 {
1258 timeout.tv_sec = curl_timeo / 1000;
1259 if (timeout.tv_sec > 1)
1260 {
1261 timeout.tv_sec = 0;
1262 timeout.tv_usec = 250000;
1263 }
1264 else
1265 {
1266 timeout.tv_usec = (curl_timeo % 1000) * 1000;
1267 }
1268 }
1269
1270 curl_multi_fdset (ykh->multi, &fdread, &fdwrite, &fdexcep, &maxfd);
1271 select (maxfd + 1, &fdread, &fdwrite, &fdexcep, &timeout);
1272
1273 while (msgs)
1274 {
1275 CURL *curl_easy;
1276 struct curl_data *data;
1277 char *url_used;
1278 char *status;
1279 CURLMsg *msg;
1280
1281 msg = curl_multi_info_read (ykh->multi, &msgs);
1282 if (!msg || msg->msg != CURLMSG_DONE)
1283 {
1284 continue;
1285 }
1286
1287 /* if we get anything other than CURLE_OK we throw away this result */
1288 if (msg->data.result != CURLE_OK)
1289 {
1290 out = YKCLIENT_CURL_PERFORM_ERROR;
1291 continue;
1292 }
1293
1294 curl_easy = msg->easy_handle;
1295
1296 curl_easy_getinfo (curl_easy, CURLINFO_PRIVATE, (char **) &data);
1297
1298 if (data == 0 || data->curl_chunk_size == 0 ||
1299 data->curl_chunk == NULL)
1300 {
1301 out = YKCLIENT_PARSE_ERROR;
1302 goto finish;
1303 }
1304
1305 curl_easy_getinfo (curl_easy, CURLINFO_EFFECTIVE_URL, &url_used);
1306 strncpy (ykc->last_url, url_used, sizeof (ykc->last_url));
1307
1308 if(ykc->srv_response)
1309 {
1310 ykclient_server_response_free (ykc->srv_response);
1311 }
1312
1313 ykc->srv_response = ykclient_server_response_init ();
1314 if (ykc->srv_response == NULL)
1315 {
1316 out = YKCLIENT_PARSE_ERROR;
1317 goto finish;
1318 }
1319
1320 out = ykclient_server_response_parse (data->curl_chunk,
1321 ykc->srv_response);
1322 if (out != YKCLIENT_OK)
1323 {
1324 goto finish;
1325 }
1326
1327 if (ykc->verify_signature != 0 &&
1328 ykclient_server_response_verify_signature (ykc->srv_response,
1329 ykc->key,
1330 ykc->keylen))
1331 {
1332 out = YKCLIENT_BAD_SERVER_SIGNATURE;
1333 goto finish;
1334 }
1335
1336 status = ykclient_server_response_get (ykc->srv_response, "status");
1337 if (!status)
1338 {
1339 out = YKCLIENT_PARSE_ERROR;
1340 goto finish;
1341 }
1342
1343 out = ykclient_parse_srv_error (status);
1344 if (out == YKCLIENT_OK)
1345 {
1346 char *server_otp;
1347
1348 /* Verify that the OTP and nonce we put in our request is echoed
1349 * in the response.
1350 *
1351 * This is to protect us from a man in the middle sending us a
1352 * previously seen genuine response again (such as an status=OK
1353 * response even though the real server will respond
1354 * status=REPLAYED_OTP in a few milliseconds.
1355 */
1356 if (nonce)
1357 {
1358 char *server_nonce =
1359 ykclient_server_response_get (ykc->srv_response,
1360 "nonce");
1361 if (server_nonce == NULL || strcmp (nonce, server_nonce))
1362 {
1363 out = YKCLIENT_HMAC_ERROR;
1364 goto finish;
1365 }
1366 }
1367
1368 server_otp = ykclient_server_response_get (ykc->srv_response, "otp");
1369 if (server_otp == NULL || strcmp (yubikey, server_otp))
1370 {
1371 out = YKCLIENT_HMAC_ERROR;
1372 }
1373
1374 goto finish;
1375 }
1376 else if ((out != YKCLIENT_PARSE_ERROR) &&
1377 (out != YKCLIENT_REPLAYED_REQUEST))
1378 {
1379 goto finish;
1380 }
1381
1382 ykclient_server_response_free (ykc->srv_response);
1383 ykc->srv_response = NULL;
1384 }
1385 }
1386 while (requests);
1387 finish:
1388 return out;
1389 }
1390
1391 /** Returns the actual URL the request was sent to
1392 *
1393 * @param ykc Yubikey client configuration.
1394 * @return the last URL a request was send to.
1395 */
1396 const char *
ykclient_get_last_url(ykclient_t * ykc)1397 ykclient_get_last_url (ykclient_t * ykc)
1398 {
1399 return ykc->last_url;
1400 }
1401
1402 /** Generates and send requests to one or more validation servers
1403 *
1404 * Sends a request to each of the servers specified by set_url_templates and
1405 * validates the response.
1406 *
1407 * @param ykc Yubikey client configuration.
1408 * @param yubikey OTP string passed to the client.
1409 * @return one of the YKCLIENT_* values or YKCLIENT_OK on success.
1410 */
1411 ykclient_rc
ykclient_request_process(ykclient_t * ykc,ykclient_handle_t * ykh,const char * yubikey)1412 ykclient_request_process (ykclient_t * ykc, ykclient_handle_t * ykh,
1413 const char *yubikey)
1414 {
1415 ykclient_rc out;
1416 char *nonce = NULL;
1417
1418 /* Generate nonce value */
1419 out = ykclient_generate_nonce (ykc, &nonce);
1420 if (out != YKCLIENT_OK)
1421 {
1422 goto finish;
1423 }
1424
1425 /* Build request/template specific URLs */
1426 out = ykclient_expand_urls (ykc, ykh, yubikey, nonce);
1427 if (out != YKCLIENT_OK)
1428 {
1429 goto finish;
1430 }
1431
1432 /* Send the request to the validation server */
1433 out = ykclient_request_send (ykc, ykh, yubikey, nonce);
1434
1435 finish:
1436 free (nonce);
1437
1438 return out;
1439 }
1440
1441 /** Generates and send requests to one or more validation servers
1442 *
1443 * Constructs a throwaway Curl handle, and sends a request to each of the
1444 * servers specified by set_url_templates.
1445 *
1446 * @note ykclient_request_process should be used for repeat requests as it
1447 * @note supports connection caching.
1448 *
1449 * @param ykc Yubikey client configuration.
1450 * @param yubikey OTP string passed to the client.
1451 * @return one of the YKCLIENT_* values or YKCLIENT_OK on success.
1452 */
1453 ykclient_rc
ykclient_request(ykclient_t * ykc,const char * yubikey)1454 ykclient_request (ykclient_t * ykc, const char *yubikey)
1455 {
1456 ykclient_rc out;
1457
1458 ykclient_handle_t *ykh;
1459
1460 /* Initialise a throw away handle */
1461 out = ykclient_handle_init (ykc, &ykh);
1462 if (out != YKCLIENT_OK)
1463 {
1464 return out;
1465 }
1466
1467 out = ykclient_request_process (ykc, ykh, yubikey);
1468
1469 ykclient_handle_done (&ykh);
1470
1471 return out;
1472 }
1473
1474 /** Extended API to validate an OTP (hexkey)
1475 *
1476 * Will default to YubiCloud validation service, but may be used
1477 * with any service, if non-NULL ykc_in pointer is passed, and
1478 * ykclient_set_url_templates is used to configure template URLs.
1479 *
1480 */
1481 ykclient_rc
ykclient_verify_otp_v2(ykclient_t * ykc_in,const char * yubikey_otp,unsigned int client_id,const char * hexkey,size_t urlcount,const char ** urls,const char * api_key)1482 ykclient_verify_otp_v2 (ykclient_t * ykc_in,
1483 const char *yubikey_otp,
1484 unsigned int client_id,
1485 const char *hexkey,
1486 size_t urlcount,
1487 const char **urls, const char *api_key)
1488 {
1489 ykclient_rc out;
1490 ykclient_t *ykc;
1491
1492
1493 if (ykc_in == NULL)
1494 {
1495 out = ykclient_init (&ykc);
1496 if (out != YKCLIENT_OK)
1497 {
1498 return out;
1499 }
1500 }
1501 else
1502 {
1503 ykc = ykc_in;
1504 }
1505
1506 ykclient_set_client_hex (ykc, client_id, hexkey);
1507
1508 if (urlcount != 0 && *urls != 0)
1509 {
1510 if (strstr (urls[0], ADD_OTP "%s"))
1511 {
1512 ykclient_set_url_templates (ykc, urlcount, urls);
1513 }
1514 else
1515 {
1516 ykclient_set_url_bases (ykc, urlcount, urls);
1517 }
1518 }
1519
1520 if (api_key)
1521 {
1522 ykclient_set_verify_signature (ykc, 1);
1523 ykclient_set_client_b64 (ykc, client_id, api_key);
1524 }
1525
1526 out = ykclient_request (ykc, yubikey_otp);
1527
1528 if (ykc_in == NULL)
1529 {
1530 ykclient_done (&ykc);
1531 }
1532
1533 return out;
1534 }
1535
1536 /** Simple API to validate an OTP (hexkey) using YubiCloud
1537 */
1538 ykclient_rc
ykclient_verify_otp(const char * yubikey_otp,unsigned int client_id,const char * hexkey)1539 ykclient_verify_otp (const char *yubikey_otp,
1540 unsigned int client_id, const char *hexkey)
1541 {
1542 return ykclient_verify_otp_v2 (NULL,
1543 yubikey_otp,
1544 client_id, hexkey, 0, NULL, NULL);
1545 }
1546
1547 /**
1548 * Fetch out server response of last query
1549 */
1550 const ykclient_server_response_t *
ykclient_get_server_response(ykclient_t * ykc)1551 ykclient_get_server_response(ykclient_t *ykc) {
1552 return ykc->srv_response;
1553 }
1554