Base code merged to SPIN 2.4
[platform/upstream/curl.git] / lib / vtls / vtls.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
10  * This software is licensed as described in the file COPYING, which
11  * you should have received as part of this distribution. The terms
12  * are also available at http://curl.haxx.se/docs/copyright.html.
13  *
14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15  * copies of the Software, and permit persons to whom the Software is
16  * furnished to do so, under the terms of the COPYING file.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  ***************************************************************************/
22
23 /* This file is for implementing all "generic" SSL functions that all libcurl
24    internals should use. It is then responsible for calling the proper
25    "backend" function.
26
27    SSL-functions in libcurl should call functions in this source file, and not
28    to any specific SSL-layer.
29
30    Curl_ssl_ - prefix for generic ones
31    Curl_ossl_ - prefix for OpenSSL ones
32    Curl_gtls_ - prefix for GnuTLS ones
33    Curl_nss_ - prefix for NSS ones
34    Curl_gskit_ - prefix for GSKit ones
35    Curl_polarssl_ - prefix for PolarSSL ones
36    Curl_cyassl_ - prefix for CyaSSL ones
37    Curl_schannel_ - prefix for Schannel SSPI ones
38    Curl_darwinssl_ - prefix for SecureTransport (Darwin) ones
39
40    Note that this source code uses curlssl_* functions, and they are all
41    defines/macros #defined by the lib-specific header files.
42
43    "SSL/TLS Strong Encryption: An Introduction"
44    http://httpd.apache.org/docs-2.0/ssl/ssl_intro.html
45 */
46
47 #include "curl_setup.h"
48
49 #ifdef HAVE_SYS_TYPES_H
50 #include <sys/types.h>
51 #endif
52 #ifdef HAVE_SYS_STAT_H
53 #include <sys/stat.h>
54 #endif
55 #ifdef HAVE_FCNTL_H
56 #include <fcntl.h>
57 #endif
58
59 #include "urldata.h"
60
61 #include "vtls.h" /* generic SSL protos etc */
62 #include "slist.h"
63 #include "sendf.h"
64 #include "rawstr.h"
65 #include "url.h"
66 #include "curl_memory.h"
67 #include "progress.h"
68 #include "share.h"
69 #include "timeval.h"
70 #include "curl_md5.h"
71 #include "warnless.h"
72 #include "curl_base64.h"
73
74 #define _MPRINTF_REPLACE /* use our functions only */
75 #include <curl/mprintf.h>
76
77 /* The last #include file should be: */
78 #include "memdebug.h"
79
80 /* convenience macro to check if this handle is using a shared SSL session */
81 #define SSLSESSION_SHARED(data) (data->share &&                        \
82                                  (data->share->specifier &             \
83                                   (1<<CURL_LOCK_DATA_SSL_SESSION)))
84
85 static bool safe_strequal(char* str1, char* str2)
86 {
87   if(str1 && str2)
88     /* both pointers point to something then compare them */
89     return (0 != Curl_raw_equal(str1, str2)) ? TRUE : FALSE;
90   else
91     /* if both pointers are NULL then treat them as equal */
92     return (!str1 && !str2) ? TRUE : FALSE;
93 }
94
95 bool
96 Curl_ssl_config_matches(struct ssl_config_data* data,
97                         struct ssl_config_data* needle)
98 {
99   if((data->version == needle->version) &&
100      (data->verifypeer == needle->verifypeer) &&
101      (data->verifyhost == needle->verifyhost) &&
102      safe_strequal(data->CApath, needle->CApath) &&
103      safe_strequal(data->CAfile, needle->CAfile) &&
104      safe_strequal(data->random_file, needle->random_file) &&
105      safe_strequal(data->egdsocket, needle->egdsocket) &&
106      safe_strequal(data->cipher_list, needle->cipher_list))
107     return TRUE;
108
109   return FALSE;
110 }
111
112 bool
113 Curl_clone_ssl_config(struct ssl_config_data *source,
114                       struct ssl_config_data *dest)
115 {
116   dest->sessionid = source->sessionid;
117   dest->verifyhost = source->verifyhost;
118   dest->verifypeer = source->verifypeer;
119   dest->version = source->version;
120
121   if(source->CAfile) {
122     dest->CAfile = strdup(source->CAfile);
123     if(!dest->CAfile)
124       return FALSE;
125   }
126   else
127     dest->CAfile = NULL;
128
129   if(source->CApath) {
130     dest->CApath = strdup(source->CApath);
131     if(!dest->CApath)
132       return FALSE;
133   }
134   else
135     dest->CApath = NULL;
136
137   if(source->cipher_list) {
138     dest->cipher_list = strdup(source->cipher_list);
139     if(!dest->cipher_list)
140       return FALSE;
141   }
142   else
143     dest->cipher_list = NULL;
144
145   if(source->egdsocket) {
146     dest->egdsocket = strdup(source->egdsocket);
147     if(!dest->egdsocket)
148       return FALSE;
149   }
150   else
151     dest->egdsocket = NULL;
152
153   if(source->random_file) {
154     dest->random_file = strdup(source->random_file);
155     if(!dest->random_file)
156       return FALSE;
157   }
158   else
159     dest->random_file = NULL;
160
161   return TRUE;
162 }
163
164 void Curl_free_ssl_config(struct ssl_config_data* sslc)
165 {
166   Curl_safefree(sslc->CAfile);
167   Curl_safefree(sslc->CApath);
168   Curl_safefree(sslc->cipher_list);
169   Curl_safefree(sslc->egdsocket);
170   Curl_safefree(sslc->random_file);
171 }
172
173
174 /*
175  * Curl_rand() returns a random unsigned integer, 32bit.
176  *
177  * This non-SSL function is put here only because this file is the only one
178  * with knowledge of what the underlying SSL libraries provide in terms of
179  * randomizers.
180  *
181  * NOTE: 'data' may be passed in as NULL when coming from external API without
182  * easy handle!
183  *
184  */
185
186 unsigned int Curl_rand(struct SessionHandle *data)
187 {
188   unsigned int r = 0;
189   static unsigned int randseed;
190   static bool seeded = FALSE;
191
192 #ifdef CURLDEBUG
193   char *force_entropy = getenv("CURL_ENTROPY");
194   if(force_entropy) {
195     if(!seeded) {
196       size_t elen = strlen(force_entropy);
197       size_t clen = sizeof(randseed);
198       size_t min = elen < clen ? elen : clen;
199       memcpy((char *)&randseed, force_entropy, min);
200       seeded = TRUE;
201     }
202     else
203       randseed++;
204     return randseed;
205   }
206 #endif
207
208   /* data may be NULL! */
209   if(!Curl_ssl_random(data, (unsigned char *)&r, sizeof(r)))
210     return r;
211
212   /* If Curl_ssl_random() returns non-zero it couldn't offer randomness and we
213      instead perform a "best effort" */
214
215 #ifdef RANDOM_FILE
216   if(!seeded) {
217     /* if there's a random file to read a seed from, use it */
218     int fd = open(RANDOM_FILE, O_RDONLY);
219     if(fd > -1) {
220       /* read random data into the randseed variable */
221       ssize_t nread = read(fd, &randseed, sizeof(randseed));
222       if(nread == sizeof(randseed))
223         seeded = TRUE;
224       close(fd);
225     }
226   }
227 #endif
228
229   if(!seeded) {
230     struct timeval now = curlx_tvnow();
231     infof(data, "WARNING: Using weak random seed\n");
232     randseed += (unsigned int)now.tv_usec + (unsigned int)now.tv_sec;
233     randseed = randseed * 1103515245 + 12345;
234     randseed = randseed * 1103515245 + 12345;
235     randseed = randseed * 1103515245 + 12345;
236     seeded = TRUE;
237   }
238
239   /* Return an unsigned 32-bit pseudo-random number. */
240   r = randseed = randseed * 1103515245 + 12345;
241   return (r << 16) | ((r >> 16) & 0xFFFF);
242 }
243
244 int Curl_ssl_backend(void)
245 {
246   return (int)CURL_SSL_BACKEND;
247 }
248
249 #ifdef USE_SSL
250
251 /* "global" init done? */
252 static bool init_ssl=FALSE;
253
254 /**
255  * Global SSL init
256  *
257  * @retval 0 error initializing SSL
258  * @retval 1 SSL initialized successfully
259  */
260 int Curl_ssl_init(void)
261 {
262   /* make sure this is only done once */
263   if(init_ssl)
264     return 1;
265   init_ssl = TRUE; /* never again */
266
267   return curlssl_init();
268 }
269
270
271 /* Global cleanup */
272 void Curl_ssl_cleanup(void)
273 {
274   if(init_ssl) {
275     /* only cleanup if we did a previous init */
276     curlssl_cleanup();
277     init_ssl = FALSE;
278   }
279 }
280
281 CURLcode
282 Curl_ssl_connect(struct connectdata *conn, int sockindex)
283 {
284   CURLcode result;
285   /* mark this is being ssl-enabled from here on. */
286   conn->ssl[sockindex].use = TRUE;
287   conn->ssl[sockindex].state = ssl_connection_negotiating;
288
289   result = curlssl_connect(conn, sockindex);
290
291   if(!result)
292     Curl_pgrsTime(conn->data, TIMER_APPCONNECT); /* SSL is connected */
293
294   return result;
295 }
296
297 CURLcode
298 Curl_ssl_connect_nonblocking(struct connectdata *conn, int sockindex,
299                              bool *done)
300 {
301   CURLcode result;
302   /* mark this is being ssl requested from here on. */
303   conn->ssl[sockindex].use = TRUE;
304 #ifdef curlssl_connect_nonblocking
305   result = curlssl_connect_nonblocking(conn, sockindex, done);
306 #else
307   *done = TRUE; /* fallback to BLOCKING */
308   result = curlssl_connect(conn, sockindex);
309 #endif /* non-blocking connect support */
310   if(!result && *done)
311     Curl_pgrsTime(conn->data, TIMER_APPCONNECT); /* SSL is connected */
312   return result;
313 }
314
315 /*
316  * Check if there's a session ID for the given connection in the cache, and if
317  * there's one suitable, it is provided. Returns TRUE when no entry matched.
318  */
319 bool Curl_ssl_getsessionid(struct connectdata *conn,
320                            void **ssl_sessionid,
321                            size_t *idsize) /* set 0 if unknown */
322 {
323   struct curl_ssl_session *check;
324   struct SessionHandle *data = conn->data;
325   size_t i;
326   long *general_age;
327   bool no_match = TRUE;
328
329   *ssl_sessionid = NULL;
330
331   if(!conn->ssl_config.sessionid)
332     /* session ID re-use is disabled */
333     return TRUE;
334
335   /* Lock if shared */
336   if(SSLSESSION_SHARED(data)) {
337     Curl_share_lock(data, CURL_LOCK_DATA_SSL_SESSION, CURL_LOCK_ACCESS_SINGLE);
338     general_age = &data->share->sessionage;
339   }
340   else
341     general_age = &data->state.sessionage;
342
343   for(i = 0; i < data->set.ssl.max_ssl_sessions; i++) {
344     check = &data->state.session[i];
345     if(!check->sessionid)
346       /* not session ID means blank entry */
347       continue;
348     if(Curl_raw_equal(conn->host.name, check->name) &&
349        (conn->remote_port == check->remote_port) &&
350        Curl_ssl_config_matches(&conn->ssl_config, &check->ssl_config)) {
351       /* yes, we have a session ID! */
352       (*general_age)++;          /* increase general age */
353       check->age = *general_age; /* set this as used in this age */
354       *ssl_sessionid = check->sessionid;
355       if(idsize)
356         *idsize = check->idsize;
357       no_match = FALSE;
358       break;
359     }
360   }
361
362   /* Unlock */
363   if(SSLSESSION_SHARED(data))
364     Curl_share_unlock(data, CURL_LOCK_DATA_SSL_SESSION);
365
366   return no_match;
367 }
368
369 /*
370  * Kill a single session ID entry in the cache.
371  */
372 void Curl_ssl_kill_session(struct curl_ssl_session *session)
373 {
374   if(session->sessionid) {
375     /* defensive check */
376
377     /* free the ID the SSL-layer specific way */
378     curlssl_session_free(session->sessionid);
379
380     session->sessionid = NULL;
381     session->age = 0; /* fresh */
382
383     Curl_free_ssl_config(&session->ssl_config);
384
385     Curl_safefree(session->name);
386   }
387 }
388
389 /*
390  * Delete the given session ID from the cache.
391  */
392 void Curl_ssl_delsessionid(struct connectdata *conn, void *ssl_sessionid)
393 {
394   size_t i;
395   struct SessionHandle *data=conn->data;
396
397   if(SSLSESSION_SHARED(data))
398     Curl_share_lock(data, CURL_LOCK_DATA_SSL_SESSION, CURL_LOCK_ACCESS_SINGLE);
399
400   for(i = 0; i < data->set.ssl.max_ssl_sessions; i++) {
401     struct curl_ssl_session *check = &data->state.session[i];
402
403     if(check->sessionid == ssl_sessionid) {
404       Curl_ssl_kill_session(check);
405       break;
406     }
407   }
408
409   if(SSLSESSION_SHARED(data))
410     Curl_share_unlock(data, CURL_LOCK_DATA_SSL_SESSION);
411 }
412
413 /*
414  * Store session id in the session cache. The ID passed on to this function
415  * must already have been extracted and allocated the proper way for the SSL
416  * layer. Curl_XXXX_session_free() will be called to free/kill the session ID
417  * later on.
418  */
419 CURLcode Curl_ssl_addsessionid(struct connectdata *conn,
420                                void *ssl_sessionid,
421                                size_t idsize)
422 {
423   size_t i;
424   struct SessionHandle *data=conn->data; /* the mother of all structs */
425   struct curl_ssl_session *store = &data->state.session[0];
426   long oldest_age=data->state.session[0].age; /* zero if unused */
427   char *clone_host;
428   long *general_age;
429
430   /* Even though session ID re-use might be disabled, that only disables USING
431      IT. We still store it here in case the re-using is again enabled for an
432      upcoming transfer */
433
434   clone_host = strdup(conn->host.name);
435   if(!clone_host)
436     return CURLE_OUT_OF_MEMORY; /* bail out */
437
438   /* Now we should add the session ID and the host name to the cache, (remove
439      the oldest if necessary) */
440
441   /* If using shared SSL session, lock! */
442   if(SSLSESSION_SHARED(data)) {
443     Curl_share_lock(data, CURL_LOCK_DATA_SSL_SESSION, CURL_LOCK_ACCESS_SINGLE);
444     general_age = &data->share->sessionage;
445   }
446   else {
447     general_age = &data->state.sessionage;
448   }
449
450   /* find an empty slot for us, or find the oldest */
451   for(i = 1; (i < data->set.ssl.max_ssl_sessions) &&
452         data->state.session[i].sessionid; i++) {
453     if(data->state.session[i].age < oldest_age) {
454       oldest_age = data->state.session[i].age;
455       store = &data->state.session[i];
456     }
457   }
458   if(i == data->set.ssl.max_ssl_sessions)
459     /* cache is full, we must "kill" the oldest entry! */
460     Curl_ssl_kill_session(store);
461   else
462     store = &data->state.session[i]; /* use this slot */
463
464   /* now init the session struct wisely */
465   store->sessionid = ssl_sessionid;
466   store->idsize = idsize;
467   store->age = *general_age;    /* set current age */
468   if(store->name)
469     /* free it if there's one already present */
470     free(store->name);
471   store->name = clone_host;               /* clone host name */
472   store->remote_port = conn->remote_port; /* port number */
473
474
475   /* Unlock */
476   if(SSLSESSION_SHARED(data))
477     Curl_share_unlock(data, CURL_LOCK_DATA_SSL_SESSION);
478
479   if(!Curl_clone_ssl_config(&conn->ssl_config, &store->ssl_config)) {
480     store->sessionid = NULL; /* let caller free sessionid */
481     free(clone_host);
482     return CURLE_OUT_OF_MEMORY;
483   }
484
485   return CURLE_OK;
486 }
487
488
489 void Curl_ssl_close_all(struct SessionHandle *data)
490 {
491   size_t i;
492   /* kill the session ID cache if not shared */
493   if(data->state.session && !SSLSESSION_SHARED(data)) {
494     for(i = 0; i < data->set.ssl.max_ssl_sessions; i++)
495       /* the single-killer function handles empty table slots */
496       Curl_ssl_kill_session(&data->state.session[i]);
497
498     /* free the cache data */
499     Curl_safefree(data->state.session);
500   }
501
502   curlssl_close_all(data);
503 }
504
505 void Curl_ssl_close(struct connectdata *conn, int sockindex)
506 {
507   DEBUGASSERT((sockindex <= 1) && (sockindex >= -1));
508   curlssl_close(conn, sockindex);
509 }
510
511 CURLcode Curl_ssl_shutdown(struct connectdata *conn, int sockindex)
512 {
513   if(curlssl_shutdown(conn, sockindex))
514     return CURLE_SSL_SHUTDOWN_FAILED;
515
516   conn->ssl[sockindex].use = FALSE; /* get back to ordinary socket usage */
517   conn->ssl[sockindex].state = ssl_connection_none;
518
519   conn->recv[sockindex] = Curl_recv_plain;
520   conn->send[sockindex] = Curl_send_plain;
521
522   return CURLE_OK;
523 }
524
525 /* Selects an SSL crypto engine
526  */
527 CURLcode Curl_ssl_set_engine(struct SessionHandle *data, const char *engine)
528 {
529   return curlssl_set_engine(data, engine);
530 }
531
532 /* Selects the default SSL crypto engine
533  */
534 CURLcode Curl_ssl_set_engine_default(struct SessionHandle *data)
535 {
536   return curlssl_set_engine_default(data);
537 }
538
539 /* Return list of OpenSSL crypto engine names. */
540 struct curl_slist *Curl_ssl_engines_list(struct SessionHandle *data)
541 {
542   return curlssl_engines_list(data);
543 }
544
545 /*
546  * This sets up a session ID cache to the specified size. Make sure this code
547  * is agnostic to what underlying SSL technology we use.
548  */
549 CURLcode Curl_ssl_initsessions(struct SessionHandle *data, size_t amount)
550 {
551   struct curl_ssl_session *session;
552
553   if(data->state.session)
554     /* this is just a precaution to prevent multiple inits */
555     return CURLE_OK;
556
557   session = calloc(amount, sizeof(struct curl_ssl_session));
558   if(!session)
559     return CURLE_OUT_OF_MEMORY;
560
561   /* store the info in the SSL section */
562   data->set.ssl.max_ssl_sessions = amount;
563   data->state.session = session;
564   data->state.sessionage = 1; /* this is brand new */
565   return CURLE_OK;
566 }
567
568 size_t Curl_ssl_version(char *buffer, size_t size)
569 {
570   return curlssl_version(buffer, size);
571 }
572
573 /*
574  * This function tries to determine connection status.
575  *
576  * Return codes:
577  *     1 means the connection is still in place
578  *     0 means the connection has been closed
579  *    -1 means the connection status is unknown
580  */
581 int Curl_ssl_check_cxn(struct connectdata *conn)
582 {
583   return curlssl_check_cxn(conn);
584 }
585
586 bool Curl_ssl_data_pending(const struct connectdata *conn,
587                            int connindex)
588 {
589   return curlssl_data_pending(conn, connindex);
590 }
591
592 void Curl_ssl_free_certinfo(struct SessionHandle *data)
593 {
594   int i;
595   struct curl_certinfo *ci = &data->info.certs;
596
597   if(ci->num_of_certs) {
598     /* free all individual lists used */
599     for(i=0; i<ci->num_of_certs; i++) {
600       curl_slist_free_all(ci->certinfo[i]);
601       ci->certinfo[i] = NULL;
602     }
603
604     free(ci->certinfo); /* free the actual array too */
605     ci->certinfo = NULL;
606     ci->num_of_certs = 0;
607   }
608 }
609
610 CURLcode Curl_ssl_init_certinfo(struct SessionHandle *data, int num)
611 {
612   struct curl_certinfo *ci = &data->info.certs;
613   struct curl_slist **table;
614
615   /* Free any previous certificate information structures */
616   Curl_ssl_free_certinfo(data);
617
618   /* Allocate the required certificate information structures */
619   table = calloc((size_t) num, sizeof(struct curl_slist *));
620   if(!table)
621     return CURLE_OUT_OF_MEMORY;
622
623   ci->num_of_certs = num;
624   ci->certinfo = table;
625
626   return CURLE_OK;
627 }
628
629 /*
630  * 'value' is NOT a zero terminated string
631  */
632 CURLcode Curl_ssl_push_certinfo_len(struct SessionHandle *data,
633                                     int certnum,
634                                     const char *label,
635                                     const char *value,
636                                     size_t valuelen)
637 {
638   struct curl_certinfo * ci = &data->info.certs;
639   char * output;
640   struct curl_slist * nl;
641   CURLcode result = CURLE_OK;
642   size_t labellen = strlen(label);
643   size_t outlen = labellen + 1 + valuelen + 1; /* label:value\0 */
644
645   output = malloc(outlen);
646   if(!output)
647     return CURLE_OUT_OF_MEMORY;
648
649   /* sprintf the label and colon */
650   snprintf(output, outlen, "%s:", label);
651
652   /* memcpy the value (it might not be zero terminated) */
653   memcpy(&output[labellen+1], value, valuelen);
654
655   /* zero terminate the output */
656   output[labellen + 1 + valuelen] = 0;
657
658   nl = Curl_slist_append_nodup(ci->certinfo[certnum], output);
659   if(!nl) {
660     free(output);
661     curl_slist_free_all(ci->certinfo[certnum]);
662     result = CURLE_OUT_OF_MEMORY;
663   }
664
665   ci->certinfo[certnum] = nl;
666   return result;
667 }
668
669 /*
670  * This is a convenience function for push_certinfo_len that takes a zero
671  * terminated value.
672  */
673 CURLcode Curl_ssl_push_certinfo(struct SessionHandle *data,
674                                 int certnum,
675                                 const char *label,
676                                 const char *value)
677 {
678   size_t valuelen = strlen(value);
679
680   return Curl_ssl_push_certinfo_len(data, certnum, label, value, valuelen);
681 }
682
683 int Curl_ssl_random(struct SessionHandle *data,
684                      unsigned char *entropy,
685                      size_t length)
686 {
687   return curlssl_random(data, entropy, length);
688 }
689
690 /*
691  * Public key pem to der conversion
692  */
693
694 static CURLcode pubkey_pem_to_der(const char *pem,
695                                   unsigned char **der, size_t *der_len)
696 {
697   char *stripped_pem, *begin_pos, *end_pos;
698   size_t pem_count, stripped_pem_count = 0, pem_len;
699   CURLcode result;
700
701   /* if no pem, exit. */
702   if(!pem)
703     return CURLE_BAD_CONTENT_ENCODING;
704
705   begin_pos = strstr(pem, "-----BEGIN PUBLIC KEY-----");
706   if(!begin_pos)
707     return CURLE_BAD_CONTENT_ENCODING;
708
709   pem_count = begin_pos - pem;
710   /* Invalid if not at beginning AND not directly following \n */
711   if(0 != pem_count && '\n' != pem[pem_count - 1])
712     return CURLE_BAD_CONTENT_ENCODING;
713
714   /* 26 is length of "-----BEGIN PUBLIC KEY-----" */
715   pem_count += 26;
716
717   /* Invalid if not directly following \n */
718   end_pos = strstr(pem + pem_count, "\n-----END PUBLIC KEY-----");
719   if(!end_pos)
720     return CURLE_BAD_CONTENT_ENCODING;
721
722   pem_len = end_pos - pem;
723
724   stripped_pem = malloc(pem_len - pem_count + 1);
725   if(!stripped_pem)
726     return CURLE_OUT_OF_MEMORY;
727
728   /*
729    * Here we loop through the pem array one character at a time between the
730    * correct indices, and place each character that is not '\n' or '\r'
731    * into the stripped_pem array, which should represent the raw base64 string
732    */
733   while(pem_count < pem_len) {
734     if('\n' != pem[pem_count] && '\r' != pem[pem_count])
735       stripped_pem[stripped_pem_count++] = pem[pem_count];
736     ++pem_count;
737   }
738   /* Place the null terminator in the correct place */
739   stripped_pem[stripped_pem_count] = '\0';
740
741   result = Curl_base64_decode(stripped_pem, der, der_len);
742
743   Curl_safefree(stripped_pem);
744
745   return result;
746 }
747
748 /*
749  * Generic pinned public key check.
750  */
751
752 CURLcode Curl_pin_peer_pubkey(const char *pinnedpubkey,
753                               const unsigned char *pubkey, size_t pubkeylen)
754 {
755   FILE *fp;
756   unsigned char *buf = NULL, *pem_ptr = NULL;
757   long filesize;
758   size_t size, pem_len;
759   CURLcode pem_read;
760   CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH;
761
762   /* if a path wasn't specified, don't pin */
763   if(!pinnedpubkey)
764     return CURLE_OK;
765   if(!pubkey || !pubkeylen)
766     return result;
767   fp = fopen(pinnedpubkey, "rb");
768   if(!fp)
769     return result;
770
771   do {
772     /* Determine the file's size */
773     if(fseek(fp, 0, SEEK_END))
774       break;
775     filesize = ftell(fp);
776     if(fseek(fp, 0, SEEK_SET))
777       break;
778     if(filesize < 0 || filesize > MAX_PINNED_PUBKEY_SIZE)
779       break;
780
781     /*
782      * if the size of our certificate is bigger than the file
783      * size then it can't match
784      */
785     size = curlx_sotouz((curl_off_t) filesize);
786     if(pubkeylen > size)
787       break;
788
789     /*
790      * Allocate buffer for the pinned key
791      * With 1 additional byte for null terminator in case of PEM key
792      */
793     buf = malloc(size + 1);
794     if(!buf)
795       break;
796
797     /* Returns number of elements read, which should be 1 */
798     if((int) fread(buf, size, 1, fp) != 1)
799       break;
800
801     /* If the sizes are the same, it can't be base64 encoded, must be der */
802     if(pubkeylen == size) {
803       if(!memcmp(pubkey, buf, pubkeylen))
804         result = CURLE_OK;
805       break;
806     }
807
808     /*
809      * Otherwise we will assume it's PEM and try to decode it
810      * after placing null terminator
811      */
812     buf[size] = '\0';
813     pem_read = pubkey_pem_to_der((const char *)buf, &pem_ptr, &pem_len);
814     /* if it wasn't read successfully, exit */
815     if(pem_read)
816       break;
817
818     /*
819      * if the size of our certificate doesn't match the size of
820      * the decoded file, they can't be the same, otherwise compare
821      */
822     if(pubkeylen == pem_len && !memcmp(pubkey, pem_ptr, pubkeylen))
823       result = CURLE_OK;
824   } while(0);
825
826   Curl_safefree(buf);
827   Curl_safefree(pem_ptr);
828   fclose(fp);
829
830   return result;
831 }
832
833 void Curl_ssl_md5sum(unsigned char *tmp, /* input */
834                      size_t tmplen,
835                      unsigned char *md5sum, /* output */
836                      size_t md5len)
837 {
838 #ifdef curlssl_md5sum
839   curlssl_md5sum(tmp, tmplen, md5sum, md5len);
840 #else
841   MD5_context *MD5pw;
842
843   (void) md5len;
844
845   MD5pw = Curl_MD5_init(Curl_DIGEST_MD5);
846   Curl_MD5_update(MD5pw, tmp, curlx_uztoui(tmplen));
847   Curl_MD5_final(MD5pw, md5sum);
848 #endif
849 }
850
851 /*
852  * Check whether the SSL backend supports the status_request extension.
853  */
854 bool Curl_ssl_cert_status_request(void)
855 {
856 #ifdef curlssl_cert_status_request
857   return curlssl_cert_status_request();
858 #else
859   return FALSE;
860 #endif
861 }
862
863 #endif /* USE_SSL */