1 /*
2  * Copyright (C) 2015-2020 Alibaba Group Holding Limited
3  */
4 
5 #include <stdio.h>
6 #include <string.h>
7 #include <sys/time.h>
8 #include <unistd.h>
9 
10 #include "httpclient.h"
11 #include "http_opts.h"
12 #include "http_wrappers.h"
13 #include <netdb.h>
14 #include <sys/socket.h>
15 
16 #if CONFIG_HTTP_SECURE
17 #include "mbedtls/net.h"
18 #include "mbedtls/ssl.h"
19 #include "mbedtls/certs.h"
20 #include "mbedtls/entropy.h"
21 #include "mbedtls/ctr_drbg.h"
22 
23 /** @brief   This structure defines the httpclient ssl structure.  */
24 typedef struct {
25     mbedtls_ssl_context ssl_ctx;        /**< mbedtls ssl context */
26     mbedtls_net_context net_ctx;        /**< fill in socket id   */
27     mbedtls_ssl_config ssl_conf;        /**< entropy context     */
28     mbedtls_entropy_context entropy;    /**< ssl configuration   */
29     mbedtls_ctr_drbg_context ctr_drbg;  /**< ctr drbg context    */
30     mbedtls_x509_crt_profile profile;   /**< x509 cacert profile */
31     mbedtls_x509_crt cacert;            /**< x509 cacert         */
32     mbedtls_x509_crt clicert;           /**< x509 client cacert  */
33     mbedtls_pk_context pkey;            /**< pkey context        */
34 } httpclient_ssl_t;
35 
36 
37 #if defined(MBEDTLS_DEBUG_C)
38 #define DEBUG_LEVEL 2
39 #endif
40 #endif
41 
42 
43 /*
44  *  Conncection wrapper function for HTTP
45  *  @param[in] client    pointer to the httpclient.
46  *  @param[in] host      host url
47  *  @return    0 success other failed
48  */
http_tcp_conn_wrapper(httpclient_t * client,const char * host)49 int http_tcp_conn_wrapper(httpclient_t *client, const char *host)
50 {
51     struct addrinfo hints, *addr_list, *cur;
52     int ret = 0;
53     char port[10] = {0};
54 
55     memset( &hints, 0, sizeof( hints ) );
56     hints.ai_family = AF_UNSPEC;
57     hints.ai_socktype = SOCK_STREAM;
58     hints.ai_protocol = IPPROTO_TCP;
59 
60     snprintf(port, sizeof(port), "%d", client->remote_port) ;
61     if ( getaddrinfo( host, port , &hints, &addr_list ) != 0 ) {
62         http_err("getaddrinfo != 0, return EDNS");
63         return HTTP_EDNS;
64     }
65 
66     /* Try the sockaddrs until a connection succeeds */
67     ret = HTTP_EDNS;
68     for ( cur = addr_list; cur != NULL; cur = cur->ai_next ) {
69         client->socket = (int) socket( cur->ai_family, cur->ai_socktype,
70                                         cur->ai_protocol );
71         if ( client->socket < 0 ) {
72             ret = HTTP_ECONN;
73             continue;
74         }
75 
76         if ( connect( client->socket, cur->ai_addr, (int)cur->ai_addrlen ) == 0 ) {
77             ret = 0;
78             break;
79         }
80 
81         close(client->socket);
82         ret = HTTP_ECONN;
83     }
84 
85     freeaddrinfo( addr_list );
86 
87     return ret;
88 }
89 
http_tcp_close_wrapper(httpclient_t * client)90 int http_tcp_close_wrapper(httpclient_t *client)
91 {
92     close(client->socket);
93     client->socket = -1;
94     return 0;
95 }
96 
http_tcp_send_wrapper(httpclient_t * client,const char * data,int length)97 int http_tcp_send_wrapper(httpclient_t *client, const char *data, int length)
98 {
99     int written_len = 0;
100 
101     while (written_len < length) {
102         int ret = send(client->socket, data + written_len, length - written_len, 0);
103         if (ret > 0) {
104             written_len += ret;
105             continue;
106         } else if (ret == 0) {
107             return written_len;
108         } else {
109             http_err("Connection err ret=%d errno=%d\n", ret, errno);
110             return -1; /* Connnection error */
111         }
112     }
113 
114     return written_len;
115 }
116 
http_tcp_recv_wrapper(httpclient_t * client,char * buf,int buflen,int timeout_ms,int * p_read_len)117 int http_tcp_recv_wrapper(httpclient_t *client, char *buf, int buflen, int timeout_ms, int *p_read_len)
118 {
119     int ret = 0, select_ret;
120     size_t readLen = 0;
121     struct timeval timeout;
122     fd_set         sets;
123     int err_record;
124     int sockfd = client->socket;
125 
126     timeout.tv_sec = timeout_ms / 1000;
127     timeout.tv_usec = 0;
128 
129     while (readLen < buflen) {
130         FD_ZERO(&sets);
131         FD_SET(sockfd, &sets);
132 
133         select_ret = select(sockfd + 1, &sets, NULL, NULL, &timeout);
134         err_record = errno;
135         if (select_ret > 0) {
136             if (0 == FD_ISSET(sockfd, &sets)) {
137                 ret = 0;
138                 http_debug("select continue");
139                 continue;
140             }
141 
142             ret = recv(sockfd, buf + readLen, buflen - readLen, 0);
143             err_record = errno;
144             if (ret < 0 ) {
145                 if ((EINTR == err_record) || (EAGAIN == err_record) || (EWOULDBLOCK == err_record) ||
146                     (EPROTOTYPE == err_record) || (EALREADY == err_record) || (EINPROGRESS == err_record)) {
147                     http_debug("recv continue %d ret %d", err_record, ret);
148                     continue;
149                 }
150             } else if (ret == 0) {
151                     http_debug("recv return 0 disconnected");
152                     ret = HTTP_ECLSD;
153             }
154         } else if (select_ret == 0) {
155             http_info("select return 0 may disconnected");
156             ret = HTTP_ECLSD;
157         } else {
158             http_debug("select return %d errno %d", select_ret, err_record);
159             if (err_record == EINTR) {
160                 continue;
161             }
162 
163             ret = select_ret;
164         }
165 
166         if (ret > 0) {
167             readLen += ret;
168         } else if (ret == HTTP_ECLSD) {
169             break;
170         } else {
171             http_err("Connection error (recv returned %d readLen:%d)", ret,readLen);
172             *p_read_len = readLen;
173             return HTTP_ECONN;
174         }
175     }
176 
177     *p_read_len = readLen;
178     if (ret == HTTP_ECLSD) {
179         return ret;
180     } else {
181         return HTTP_SUCCESS;
182     }
183 }
184 
185 #if CONFIG_HTTP_SECURE
186 
http_ssl_send_wrapper(httpclient_t * client,const char * data,size_t length)187 int http_ssl_send_wrapper(httpclient_t *client, const char *data, size_t length)
188 {
189     size_t written_len = 0;
190 
191     httpclient_ssl_t *ssl = (httpclient_ssl_t *) client->ssl;
192     if (!ssl) {
193     	return -1;
194     }
195 
196     while (written_len < length) {
197         int ret = mbedtls_ssl_write(&ssl->ssl_ctx, (unsigned char *)(data + written_len), (length - written_len));
198         if (ret > 0) {
199             written_len += ret;
200             continue;
201         } else if (ret == 0) {
202             return written_len;
203         } else {
204             return -1; /* Connnection error */
205         }
206     }
207 
208     return written_len;
209 }
210 
httpclient_debug(void * ctx,int level,const char * file,int line,const char * str)211 static void httpclient_debug( void *ctx, int level, const char *file, int line, const char *str )
212 {
213     http_debug("%s", str);
214 }
215 
httpclient_random(void * prng,unsigned char * output,size_t output_len)216 static int httpclient_random(void *prng, unsigned char *output, size_t output_len)
217 {
218     uint32_t rnglen = output_len;
219     uint8_t rngoffset = 0;
220     struct timeval time;
221 
222     memset(&time, 0, sizeof(struct timeval));
223     gettimeofday(&time, NULL);
224 
225     aos_srand((unsigned int)(time.tv_sec * 1000 + time.tv_usec / 1000) + aos_rand());
226 
227     while (rnglen > 0) {
228         *(output + rngoffset) = (uint8_t)aos_rand();
229         rngoffset++;
230         rnglen--;
231     }
232 
233     return 0;
234 }
235 
http_ssl_conn_wrapper(httpclient_t * client,const char * host)236 int http_ssl_conn_wrapper(httpclient_t *client, const char *host)
237 {
238     int authmode = MBEDTLS_SSL_VERIFY_NONE;
239 #ifdef MBEDTLS_ENTROPY_C
240     const char *pers = "https";
241 #endif
242     int value, ret = 0;
243     uint32_t flags;
244     char port[10] = {0};
245     httpclient_ssl_t *ssl;
246 
247     client->ssl = (httpclient_ssl_t *)calloc(1, sizeof(httpclient_ssl_t));
248 
249     if (!client->ssl) {
250         http_err("Memory malloc error.");
251         ret = -1;
252         goto exit;
253     }
254     ssl = (httpclient_ssl_t *)client->ssl;
255 
256     if (client->server_cert)
257         authmode = MBEDTLS_SSL_VERIFY_REQUIRED;
258 
259     /*
260      * Initialize the RNG and the session data
261      */
262 #if defined(MBEDTLS_DEBUG_C)
263     mbedtls_debug_set_threshold(DEBUG_LEVEL);
264 #endif
265     mbedtls_net_init(&ssl->net_ctx);
266     mbedtls_ssl_init(&ssl->ssl_ctx);
267     mbedtls_ssl_config_init(&ssl->ssl_conf);
268 #ifdef MBEDTLS_X509_CRT_PARSE_C
269     mbedtls_x509_crt_init(&ssl->cacert);
270     mbedtls_x509_crt_init(&ssl->clicert);
271 #endif
272     mbedtls_pk_init(&ssl->pkey);
273 #ifdef MBEDTLS_ENTROPY_C
274     mbedtls_ctr_drbg_init(&ssl->ctr_drbg);
275     mbedtls_entropy_init(&ssl->entropy);
276     if ((value = mbedtls_ctr_drbg_seed(&ssl->ctr_drbg,
277                                mbedtls_entropy_func,
278                                &ssl->entropy,
279                                (const unsigned char*)pers,
280                                strlen(pers))) != 0) {
281         http_err("mbedtls_ctr_drbg_seed() failed, value:-0x%x.", -value);
282         ret = -1;
283         goto exit;
284     }
285 #endif
286     /*
287     * Load the Client certificate
288     */
289     if (client->client_cert && client->client_pk) {
290 #ifdef MBEDTLS_X509_CRT_PARSE_C
291         ret = mbedtls_x509_crt_parse(&ssl->clicert, (const unsigned char *)client->client_cert, client->client_cert_len);
292         if (ret < 0) {
293             http_err("Loading cli_cert failed! mbedtls_x509_crt_parse returned -0x%x.", -ret);
294             ret = -1;
295             goto exit;
296         }
297 #endif
298         ret = mbedtls_pk_parse_key(&ssl->pkey, (const unsigned char *)client->client_pk, client->client_pk_len, NULL, 0);
299         if (ret != 0) {
300             http_err("failed! mbedtls_pk_parse_key returned -0x%x.", -ret);
301             ret = -1;
302             goto exit;
303         }
304     }
305 
306     /*
307     * Load the trusted CA
308     */
309     /* cert_len passed in is gotten from sizeof not strlen */
310 #ifdef MBEDTLS_X509_CRT_PARSE_C
311     if (client->server_cert && ((value = mbedtls_x509_crt_parse(&ssl->cacert,
312                                         (const unsigned char *)client->server_cert,
313                                         client->server_cert_len)) < 0)) {
314         http_err("mbedtls_x509_crt_parse() failed, value:-0x%x.", -value);
315         ret = -1;
316         goto exit;
317     }
318 #endif
319 
320     /*
321      * Start the connection
322      */
323     snprintf(port, sizeof(port), "%d", client->remote_port) ;
324     if ((ret = mbedtls_net_connect(&ssl->net_ctx, host, port, MBEDTLS_NET_PROTO_TCP)) != 0) {
325         http_err("failed! mbedtls_net_connect returned %d, port:%s.", ret, port);
326         ret = -1;
327         goto exit;
328     }
329 
330     client->socket = ssl->net_ctx.fd;
331 
332     /*
333      * Setup stuff
334      */
335     if ((value = mbedtls_ssl_config_defaults(&ssl->ssl_conf,
336                                            MBEDTLS_SSL_IS_CLIENT,
337                                            MBEDTLS_SSL_TRANSPORT_STREAM,
338                                            MBEDTLS_SSL_PRESET_DEFAULT)) != 0) {
339         http_err("mbedtls_ssl_config_defaults() failed, value:-0x%x.", -value);
340         ret = -1;
341         goto exit;
342     }
343 
344     // TODO: add customerization encryption algorithm
345 #ifdef MBEDTLS_X509_CRT_PARSE_C
346     memcpy(&ssl->profile, ssl->ssl_conf.cert_profile, sizeof(mbedtls_x509_crt_profile));
347     ssl->profile.allowed_mds = ssl->profile.allowed_mds | MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_MD5);
348     mbedtls_ssl_conf_cert_profile(&ssl->ssl_conf, &ssl->profile);
349 
350     mbedtls_ssl_conf_authmode(&ssl->ssl_conf, authmode);
351     mbedtls_ssl_conf_ca_chain(&ssl->ssl_conf, &ssl->cacert, NULL);
352 
353     if (client->client_cert && (ret = mbedtls_ssl_conf_own_cert(&ssl->ssl_conf, &ssl->clicert, &ssl->pkey)) != 0) {
354         http_err(" failed! mbedtls_ssl_conf_own_cert returned %d.", ret );
355         ret = -1;
356         goto exit;
357     }
358 #endif
359 
360     mbedtls_ssl_conf_rng(&ssl->ssl_conf, httpclient_random, &ssl->ctr_drbg);
361     mbedtls_ssl_conf_dbg(&ssl->ssl_conf, httpclient_debug, NULL);
362 
363     if ((value = mbedtls_ssl_setup(&ssl->ssl_ctx, &ssl->ssl_conf)) != 0) {
364         http_err("mbedtls_ssl_setup() failed, value:-0x%x.", -value);
365         ret = -1;
366         goto exit;
367     }
368 
369     mbedtls_ssl_set_bio(&ssl->ssl_ctx, &ssl->net_ctx, mbedtls_net_send, mbedtls_net_recv, mbedtls_net_recv_timeout);
370     mbedtls_ssl_conf_read_timeout(&ssl->ssl_conf, 10000);
371 
372     /*
373     * Handshake
374     */
375     while ((ret = mbedtls_ssl_handshake(&ssl->ssl_ctx)) != 0) {
376         if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
377             http_err("mbedtls_ssl_handshake() failed, ret:-0x%x.", -ret);
378             ret = -1;
379             goto exit;
380         }
381     }
382 
383     /*
384      * Verify the server certificate
385      */
386     if ((flags = mbedtls_ssl_get_verify_result(&ssl->ssl_ctx)) != 0) {
387         char vrfy_buf[512];
388         http_err("svr_cert varification failed.");
389         mbedtls_x509_crt_verify_info(vrfy_buf, sizeof(vrfy_buf), "  ! ", flags);
390         http_debug("%s", vrfy_buf);
391         ret = -1;
392     } else {
393         http_info("svr_cert varification ok.");
394     }
395 
396 exit:
397     if (ret != 0) {
398         http_debug("ret=%d.", ret);
399         http_ssl_close_wrapper(client);
400     }
401     return ret;
402 }
403 
http_ssl_close_wrapper(httpclient_t * client)404 int http_ssl_close_wrapper(httpclient_t *client)
405 {
406     httpclient_ssl_t *ssl = (httpclient_ssl_t *)client->ssl;
407 
408     if (!ssl)
409         return -1;
410 
411     client->ssl = NULL;
412     client->client_cert = NULL;
413     client->server_cert = NULL;
414     client->client_pk = NULL;
415     client->socket = -1;
416 
417     mbedtls_ssl_close_notify(&ssl->ssl_ctx);
418     mbedtls_net_free(&ssl->net_ctx);
419 #ifdef MBEDTLS_X509_CRT_PARSE_C
420     mbedtls_x509_crt_free(&ssl->cacert);
421     mbedtls_x509_crt_free(&ssl->clicert);
422 #endif
423     mbedtls_pk_free(&ssl->pkey);
424     mbedtls_ssl_free(&ssl->ssl_ctx);
425     mbedtls_ssl_config_free(&ssl->ssl_conf);
426 #ifdef MBEDTLS_ENTROPY_C
427     mbedtls_ctr_drbg_free(&ssl->ctr_drbg);
428     mbedtls_entropy_free(&ssl->entropy);
429 #endif
430     free(ssl);
431     ssl = NULL;
432     return 0;
433 }
434 
http_ssl_recv_wrapper(httpclient_t * client,char * buf,int buflen,int timeout_ms,int * p_read_len)435 int http_ssl_recv_wrapper(httpclient_t *client, char *buf, int buflen, int timeout_ms, int *p_read_len)
436 {
437     int ret = 0;
438     size_t readLen = 0;
439 
440     httpclient_ssl_t *ssl = (httpclient_ssl_t *) client->ssl;
441     if (!ssl) {
442     	return HTTP_ECONN;
443     }
444 
445     mbedtls_ssl_conf_read_timeout(&ssl->ssl_conf, timeout_ms);
446 
447     while (readLen < buflen) {
448         ret = mbedtls_ssl_read(&ssl->ssl_ctx, (unsigned char *)buf + readLen, buflen - readLen);
449         if (ret == 0) {
450             ret = HTTP_ECLSD;
451         } else if (ret < 0) {
452             http_debug("mbedtls_ssl_read, return:%d", ret);
453             if ((MBEDTLS_ERR_SSL_TIMEOUT == ret)
454                  || (MBEDTLS_ERR_SSL_CONN_EOF == ret)
455                  || (MBEDTLS_ERR_SSL_SESSION_TICKET_EXPIRED == ret)
456                  || (MBEDTLS_ERR_SSL_NON_FATAL == ret)) {
457                 /* read already complete */
458                 /* if call mbedtls_ssl_read again, it will return 0 (means EOF) */
459             }
460 
461             if (MBEDTLS_ERR_SSL_WANT_READ == ret) {
462                 continue;
463             }
464 
465             if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) {
466                 ret = HTTP_ECLSD;
467             }
468         }
469 
470         if (ret > 0) {
471             readLen += ret;
472         } else if (ret == HTTP_ECLSD) {
473             break;
474         } else {
475             http_err("Connection error (recv returned %d readLen:%d)", ret,readLen);
476             *p_read_len = readLen;
477             return HTTP_ECONN;
478         }
479     }
480 
481     *p_read_len = readLen;
482 
483     if (ret == HTTP_ECLSD) {
484         return ret;
485     } else {
486         return HTTP_SUCCESS;
487     }
488 }
489 
490 #endif
491