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 "&timestamp=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