src/sock_gnutls.c
branchnew-transport
changeset 155 c59d3eaff0fb
parent 140 aa390e52eda8
child 156 6534a4ac957b
equal deleted inserted replaced
154:f4472119de3b 155:c59d3eaff0fb
    11 #include <time.h>
    11 #include <time.h>
    12 
    12 
    13 #include <assert.h>
    13 #include <assert.h>
    14 
    14 
    15 /**
    15 /**
    16  * Register for events based on the session's gnutls_record_get_direction().
    16  * Enable the TCP events based on the session's gnutls_record_get_direction().
    17  */
    17  */
    18 static err_t sock_gnutls_ev_enable (struct sock_gnutls *sock, struct error_info *err)
    18 static err_t sock_gnutls_ev_enable (struct sock_gnutls *sock, error_t *err)
    19 {
    19 {
    20     int ret;
    20     int ret;
       
    21     short mask;
    21 
    22 
    22     // gnutls_record_get_direction tells us what I/O operation gnutls would have required for the last
    23     // gnutls_record_get_direction tells us what I/O operation gnutls would have required for the last
    23     // operation, so we can use that to determine what events to register
    24     // operation, so we can use that to determine what events to register
    24     switch ((ret = gnutls_record_get_direction(sock->session))) {
    25     switch ((ret = gnutls_record_get_direction(sock->session))) {
    25         case 0: 
    26         case 0: 
    26             // read more data
    27             // read more data
    27             sock_fd_enable_events(SOCK_GNUTLS_FD(sock), EV_READ); 
    28             mask = EV_READ;
    28             break;
    29             break;
    29         
    30         
    30         case 1:
    31         case 1:
    31             // write buffer full
    32             // write buffer full
    32             sock_fd_enable_events(SOCK_GNUTLS_FD(sock), EV_WRITE);
    33             mask = EV_WRITE;
    33             break;
    34             break;
    34         
    35         
    35         default:
    36         default:
    36             // random error
    37             // random error
    37             RETURN_SET_ERROR_EXTRA(err, ERR_GNUTLS_RECORD_GET_DIRECTION, ret);
    38             RETURN_SET_ERROR_EXTRA(err, ERR_GNUTLS_RECORD_GET_DIRECTION, ret);
    38     }
    39     }
    39     
    40     
    40     // ok... wait
    41     // do the enabling
       
    42     if ((ERROR_CODE(err) = transport_fd_enable(SOCK_GNUTLS_FD(sock), EV_READ)))
       
    43         return ERROR_CODE(err);
       
    44     
       
    45 
    41     return SUCCESS;
    46     return SUCCESS;
    42 }
    47 }
    43 
    48 
    44 /**
    49 /**
    45  * Translate a set of gnutls_certificate_status_t values to a constant error message
    50  * Translate a set of gnutls_certificate_status_t values to a constant error message
    70 /**
    75 /**
    71  * Perform the certificate validation procedure on the socket.
    76  * Perform the certificate validation procedure on the socket.
    72  *
    77  *
    73  * Based on the GnuTLS examples/ex-rfc2818.c
    78  * Based on the GnuTLS examples/ex-rfc2818.c
    74  */
    79  */
    75 static err_t sock_gnutls_verify (struct sock_gnutls *sock, struct error_info *err)
    80 static err_t sock_gnutls_verify (struct sock_gnutls *sock, error_t *err)
    76 {
    81 {
    77     unsigned int status;
    82     unsigned int status;
    78     const gnutls_datum_t *cert_list;
    83     const gnutls_datum_t *cert_list;
    79     unsigned int cert_list_size;
    84     unsigned int cert_list_size;
    80     gnutls_x509_crt_t cert = NULL;
    85     gnutls_x509_crt_t cert = NULL;
   136  * If the sock is marked as verify, this will perform the verification, returning on any errors, and then unset the
   141  * If the sock is marked as verify, this will perform the verification, returning on any errors, and then unset the
   137  * verify flag - this ensures that the peer cert is only verified once per connection...
   142  * verify flag - this ensures that the peer cert is only verified once per connection...
   138  *
   143  *
   139  * @return >0 for finished handshake, 0 for handshake-in-progress, -err_t for errors.
   144  * @return >0 for finished handshake, 0 for handshake-in-progress, -err_t for errors.
   140  */
   145  */
   141 static int sock_gnutls_handshake (struct sock_gnutls *sock, struct error_info *err)
   146 static int sock_gnutls_handshake (struct sock_gnutls *sock, error_t *err)
   142 {
   147 {
   143     int ret;
   148     int ret;
   144 
   149 
   145     // perform the handshake
   150     // perform the handshake
   146     if ((ret = gnutls_handshake(sock->session)) < 0 && ret != GNUTLS_E_AGAIN)
   151     if ((ret = gnutls_handshake(sock->session)) < 0 && ret != GNUTLS_E_AGAIN)
   175 error:
   180 error:
   176     return -ERROR_CODE(err);    
   181     return -ERROR_CODE(err);    
   177 }
   182 }
   178 
   183 
   179 /**
   184 /**
   180  * Our SOCK_STREAM event handler. Drive the handshake if that's current, otherwise, invoke user callbacks.
   185  * Our transport_fd event handler. Drive the handshake if that's current, otherwise, invoke user callbacks.
   181  *
   186  */
   182  * XXX: this is ugly. This sock_stream-level separation doesn't really work that well.
   187 static void sock_gnutls_on_event (struct transport_fd *fd, short what, void *arg)
   183  */
       
   184 static void sock_gnutls_event_handler (int fd, short what, void *arg)
       
   185 {
   188 {
   186     struct sock_gnutls *sock = arg;
   189     struct sock_gnutls *sock = arg;
   187     struct error_info err;
   190     error_t err;
   188 
   191 
   189     (void) fd;
   192     (void) fd;
       
   193 
       
   194     // XXX: timeouts
   190     (void) what;
   195     (void) what;
   191 
   196 
   192     // are we in the handshake cycle?
   197     // are we in the handshake cycle?
   193     if (sock->handshake) {
   198     if (sock->handshake) {
   194         RESET_ERROR(&err);
   199         RESET_ERROR(&err);
   195 
   200 
       
   201         // perform the next handshake step
   196         if (sock_gnutls_handshake(sock, &err) == 0) {
   202         if (sock_gnutls_handshake(sock, &err) == 0) {
   197             // wait for the next handshake step
   203             // handshake continues
   198         
   204         
   199         } else if (SOCK_GNUTLS_BASE(sock)->conn_cb_func) {
   205         } else if (SOCK_GNUTLS_TRANSPORT(sock)->connected) {
   200             // the async connect process has now completed, either succesfully or with an error
   206             // the async connect process has now completed, either succesfully or with an error
   201             // invoke the user connect callback directly with appropriate error
   207             // invoke the user connect callback directly with appropriate error
   202             sock_stream_invoke_conn_cb(SOCK_GNUTLS_BASE(sock), ERROR_CODE(&err) ? &err : NULL, true);
   208             transport_connected(SOCK_GNUTLS_TRANSPORT(sock), ERROR_CODE(&err) ? &err : NULL, true);
   203 
   209 
   204         } else {
   210         } else {
   205             // re-handshake completed, so continue with the sock_stream_callbacks, so the user can call sock_gnutls_read/write
       
   206 
       
   207             if (ERROR_CODE(&err))
   211             if (ERROR_CODE(&err))
   208                 // XXX: bad, since we can't report this directly... we need to let the user call _read/write, and get
   212                 // the re-handshake failed, so this transport is dead
   209                 // the error from there
   213                 transport_error(SOCK_GNUTLS_TRANSPORT(sock), &err);
   210                 log_warn_err(&err, "sock_gnutls_handshake failed");
   214         
   211             
   215             else
   212             // continue where we left off
   216                 // re-handshake completed, so continue with the transport_callbacks
   213             sock_stream_invoke_callbacks(SOCK_GNUTLS_BASE(sock), sock->ev_mask);
   217                 transport_fd_invoke(SOCK_GNUTLS_FD(sock), what);
   214         }
   218         }
   215 
   219 
   216     } else {
   220     } else {
   217         // normal sock_stream operation
   221         // normal sock_stream operation
   218         // gnutls might be able to proceed now, so ask user to try what didn't work before now, using the mask given to
   222         // gnutls might be able to proceed now, so invoke user callbacks
   219         // event_enable().
   223         transport_fd_invoke(SOCK_GNUTLS_FD(sock), what);
   220         sock_stream_invoke_callbacks(SOCK_GNUTLS_BASE(sock), sock->ev_mask);
   224     }
   221     }
   225 }
   222 }
   226 
   223 
   227 static err_t sock_gnutls_read (transport_t *transport, void *buf, size_t *len, error_t *err)
   224 static err_t sock_gnutls_read (struct sock_stream *base_sock, void *buf, size_t *len, struct error_info *err)
   228 {
   225 {
   229     struct sock_gnutls *sock = transport_check(transport, &sock_gnutls_type);
   226     struct sock_gnutls *sock = SOCK_FROM_BASE(base_sock, struct sock_gnutls);
       
   227     int ret;
   230     int ret;
   228     
   231     
   229     // read gnutls record
   232     // read gnutls record
   230     ret = gnutls_record_recv(sock->session, buf, *len);
   233     do {
       
   234         ret = gnutls_record_recv(sock->session, buf, *len);
       
   235 
       
   236     } while (ret == GNUTLS_E_INTERRUPTED);
   231     
   237     
   232     // errors
   238     // errors
   233     // XXX: E_INTERRUPTED, E_REHANDSHAKE?
   239     // XXX: E_REHANDSHAKE?
       
   240     if (ret < 0 && ret != GNUTLS_E_AGAIN)
       
   241         RETURN_SET_ERROR_EXTRA(err, ERR_GNUTLS_RECORD_RECV, ret);
       
   242     
       
   243     else if (ret == 0)
       
   244         return SET_ERROR(err, ERR_READ_EOF);
       
   245 
       
   246 
       
   247     // EAGAIN?
       
   248     if (ret < 0) {
       
   249         *len = 0;
       
   250 
       
   251     } else {
       
   252         // updated length
       
   253         *len = ret;
       
   254 
       
   255     }
       
   256 
       
   257     return SUCCESS;
       
   258 }
       
   259 
       
   260 static err_t sock_gnutls_write (transport_t *transport, const void *buf, size_t *len, error_t *err)
       
   261 {
       
   262     struct sock_gnutls *sock = transport_check(transport, &sock_gnutls_type);
       
   263     int ret;
       
   264  
       
   265     // read gnutls record
       
   266     do {
       
   267         ret = gnutls_record_send(sock->session, buf, *len);
       
   268    
       
   269     } while (ret == GNUTLS_E_INTERRUPTED);
       
   270 
       
   271     // errors
   234     if (ret < 0 && ret != GNUTLS_E_AGAIN)
   272     if (ret < 0 && ret != GNUTLS_E_AGAIN)
   235         RETURN_SET_ERROR_EXTRA(err, ERR_GNUTLS_RECORD_RECV, ret);
   273         RETURN_SET_ERROR_EXTRA(err, ERR_GNUTLS_RECORD_RECV, ret);
   236     
   274     
   237     else if (ret == 0)
   275     else if (ret == 0)
   238         return SET_ERROR(err, ERR_READ_EOF);
   276         return SET_ERROR(err, ERR_READ_EOF);
   243         *len = 0;
   281         *len = 0;
   244 
   282 
   245     } else {
   283     } else {
   246         // updated length
   284         // updated length
   247         *len = ret;
   285         *len = ret;
   248 
       
   249     }
   286     }
   250 
   287 
   251     return SUCCESS;
   288     return SUCCESS;
   252 }
   289 }
   253 
   290 
   254 static err_t sock_gnutls_write (struct sock_stream *base_sock, const void *buf, size_t *len, struct error_info *err)
   291 static void _sock_gnutls_destroy (transport_t *transport)
   255 {
   292 {
   256     struct sock_gnutls *sock = SOCK_FROM_BASE(base_sock, struct sock_gnutls);
   293     struct sock_gnutls *sock = transport_check(transport, &sock_gnutls_type);
   257     int ret;
   294     
   258  
   295     // die
   259     // read gnutls record
       
   260     ret = gnutls_record_send(sock->session, buf, *len);
       
   261     
       
   262     // errors
       
   263     if (ret < 0 && ret != GNUTLS_E_AGAIN)
       
   264         RETURN_SET_ERROR_EXTRA(err, ERR_GNUTLS_RECORD_RECV, ret);
       
   265     
       
   266     else if (ret == 0)
       
   267         return SET_ERROR(err, ERR_READ_EOF);
       
   268 
       
   269 
       
   270     // eagain?
       
   271     if (ret < 0) {
       
   272         *len = 0;
       
   273 
       
   274     } else {
       
   275         // updated length
       
   276         *len = ret;
       
   277     }
       
   278 
       
   279     return SUCCESS;
       
   280 }
       
   281 
       
   282 static err_t sock_gnutls_event_init (struct sock_stream *base_sock)
       
   283 {
       
   284     struct sock_gnutls *sock = SOCK_FROM_BASE(base_sock, struct sock_gnutls);
       
   285 
       
   286     (void) sock;
       
   287 
       
   288     // already setup, ok
       
   289     return SUCCESS;
       
   290 }
       
   291 
       
   292 static err_t sock_gnutls_event_enable (struct sock_stream *base_sock, short mask)
       
   293 {
       
   294     struct sock_gnutls *sock = SOCK_FROM_BASE(base_sock, struct sock_gnutls);
       
   295     
       
   296     // store the ev_mask. We don't care about it here, because we assume that event_enable is only called once read or
       
   297     // write, respectively, return zero. This is really the only case we can handle with gnutls.
       
   298     sock->ev_mask = mask;
       
   299     
       
   300     // then wait for the event
       
   301     return sock_gnutls_ev_enable(sock, SOCK_GNUTLS_ERR(sock));
       
   302 }
       
   303 
       
   304 static void sock_gnutls_release (struct sock_stream *base_sock)
       
   305 {
       
   306     struct sock_gnutls *sock = SOCK_FROM_BASE(base_sock, struct sock_gnutls);
       
   307     
       
   308     // DIEEEE
       
   309     sock_gnutls_destroy(sock);
   296     sock_gnutls_destroy(sock);
   310 }
   297 }
   311 
   298 
   312 /**
   299 /**
   313  * Our sock_tcp-invoked connect handler
   300  * Our sock_tcp-invoked connect handler
   314  */
   301  */
   315 static void sock_gnutls_on_connect (struct sock_stream *base_sock, struct error_info *tcp_err)
   302 static void sock_gnutls__connected (transport_t *transport, const error_t *tcp_err)
   316 {
   303 {
   317     struct sock_gnutls *sock = SOCK_FROM_BASE(base_sock, struct sock_gnutls);
   304     struct sock_gnutls *sock = transport_check(transport, &sock_gnutls_type);
   318     struct error_info err;
   305     error_t err;
   319 
   306 
   320     // trap errors to let the user handle them directly
   307     // trap errors to let the user handle them directly
   321     if (tcp_err)
   308     if (tcp_err)
   322         JUMP_SET_ERROR_INFO(&err, tcp_err);
   309         JUMP_SET_ERROR_INFO(&err, tcp_err);
   323     
   310     
   324     // bind default transport functions (recv/send) to use the TCP fd
   311     // bind default transport functions (recv/send) to use the TCP fd
   325     gnutls_transport_set_ptr(sock->session, (gnutls_transport_ptr_t) (long int) SOCK_GNUTLS_FD(sock)->fd);
   312     gnutls_transport_set_ptr(sock->session, (gnutls_transport_ptr_t) (long int) SOCK_GNUTLS_FD(sock)->fd);
   326 
   313 
   327     // add ourselves as the event handler
   314     // add ourselves as the event handler
   328     if ((ERROR_CODE(&err) = sock_fd_init_ev(SOCK_GNUTLS_FD(sock), &sock_gnutls_event_handler, sock)))
   315     if ((ERROR_CODE(&err) = transport_fd_setup(SOCK_GNUTLS_FD(sock), sock_gnutls_on_event, sock)))
   329         goto error;
   316         goto error;
   330 
   317 
   331     // start handshake
   318     // start handshake
   332     if (sock_gnutls_handshake(sock, &err))
   319     if (sock_gnutls_handshake(sock, &err))
   333         // this should complete with SUCCESS if it returns >0
   320         // this should complete with SUCCESS if it returns >0
   336     // ok, so we wait...
   323     // ok, so we wait...
   337     return;
   324     return;
   338 
   325 
   339 error:
   326 error:
   340     // tell the user
   327     // tell the user
   341     SOCK_GNUTLS_BASE(sock)->conn_cb_func(SOCK_GNUTLS_BASE(sock), &err, SOCK_GNUTLS_BASE(sock)->conn_cb_arg);
   328     transport_connected(transport, &err, true);
   342 }
   329 }
   343 
   330 
   344 /*
   331 struct transport_type sock_gnutls_type = {
   345  * Our sock_stream_Type
       
   346  */
       
   347 struct sock_stream_type sock_gnutls_type = {
       
   348     .methods                = {
   332     .methods                = {
   349         .read               = &sock_gnutls_read,
   333         .read               = sock_gnutls_read,
   350         .write              = &sock_gnutls_write,
   334         .write              = sock_gnutls_write,
   351         .event_init         = &sock_gnutls_event_init,
   335         .destroy            = _sock_gnutls_destroy,
   352         .event_enable       = &sock_gnutls_event_enable,
   336         ._connected         = sock_gnutls__connected,
   353         .release            = &sock_gnutls_release,
       
   354         ._conn_cb           = &sock_gnutls_on_connect,
       
   355     },
   337     },
   356 };
   338 };
   357 
   339 
   358 /*
   340 /*
   359  * Global shared anonymous client credentials
   341  * Global shared anonymous client credentials
   364 void _log (int level, const char *msg)
   346 void _log (int level, const char *msg)
   365 {
   347 {
   366     printf("gnutls: %d: %s", level, msg);
   348     printf("gnutls: %d: %s", level, msg);
   367 }
   349 }
   368 
   350 
   369 err_t sock_gnutls_global_init (struct error_info *err)
   351 err_t sock_gnutls_global_init (error_t *err)
   370 {
   352 {
   371     // global init
   353     // global init
   372     if ((ERROR_EXTRA(err) = gnutls_global_init()) < 0)
   354     if ((ERROR_EXTRA(err) = gnutls_global_init()) < 0)
   373         return SET_ERROR(err, ERR_GNUTLS_GLOBAL_INIT);
   355         return SET_ERROR(err, ERR_GNUTLS_GLOBAL_INIT);
   374 
   356 
   393 }
   375 }
   394 
   376 
   395 err_t sock_ssl_client_cred_create (struct sock_ssl_client_cred **ctx_cred,
   377 err_t sock_ssl_client_cred_create (struct sock_ssl_client_cred **ctx_cred,
   396         const char *cafile_path, bool verify,
   378         const char *cafile_path, bool verify,
   397         const char *cert_path, const char *pkey_path,
   379         const char *cert_path, const char *pkey_path,
   398         struct error_info *err
   380         error_t *err
   399 ) {
   381 ) {
   400     struct sock_ssl_client_cred *cred;
   382     struct sock_ssl_client_cred *cred;
   401 
   383 
   402     // alloc it
   384     // alloc it
   403     if ((cred = calloc(1, sizeof(*cred))) == NULL)
   385     if ((cred = calloc(1, sizeof(*cred))) == NULL)
   451 {
   433 {
   452     if (--cred->refcount == 0)
   434     if (--cred->refcount == 0)
   453         sock_ssl_client_cred_destroy(cred);
   435         sock_ssl_client_cred_destroy(cred);
   454 }
   436 }
   455 
   437 
   456 err_t sock_ssl_connect_async (struct sock_stream **sock_ptr, 
   438 err_t sock_ssl_connect (const struct transport_info *info, transport_t **transport_ptr, 
   457         const char *hostname, const char *service,
   439         const char *hostname, const char *service,
   458         struct sock_ssl_client_cred *cred,
   440         struct sock_ssl_client_cred *cred,
   459         sock_stream_connect_cb cb_func, void *cb_arg, 
   441         error_t *err
   460         struct error_info *err
       
   461     )
   442     )
   462 {
   443 {
   463     struct sock_gnutls *sock = NULL;
   444     struct sock_gnutls *sock = NULL;
   464 
   445 
   465     // alloc
   446     // alloc
   466     if ((sock = calloc(1, sizeof(*sock))) == NULL)
   447     if ((sock = calloc(1, sizeof(*sock))) == NULL)
   467         return SET_ERROR(err, ERR_CALLOC);
   448         return SET_ERROR(err, ERR_CALLOC);
   468 
   449 
   469     // initialize base
   450     // initialize base
   470     sock_stream_init(SOCK_GNUTLS_BASE(sock), &sock_gnutls_type, cb_func, cb_arg);
   451     transport_init(SOCK_GNUTLS_TRANSPORT(sock), &sock_gnutls_type, info);
   471 
   452 
   472     if (!cred) {
   453     if (!cred) {
   473         // default credentials
   454         // default credentials
   474         cred = &sock_gnutls_client_cred_anon;
   455         cred = &sock_gnutls_client_cred_anon;
   475     
   456     
   485 
   466 
   486     // init
   467     // init
   487     if ((sock->hostname = strdup(hostname)) == NULL)
   468     if ((sock->hostname = strdup(hostname)) == NULL)
   488         JUMP_SET_ERROR(err, ERR_STRDUP);
   469         JUMP_SET_ERROR(err, ERR_STRDUP);
   489 
   470 
       
   471     // initialize TCP
       
   472     sock_tcp_init(SOCK_GNUTLS_TCP(sock));
       
   473 
   490     // initialize client session
   474     // initialize client session
   491     if ((ERROR_EXTRA(err) = gnutls_init(&sock->session, GNUTLS_CLIENT)) < 0)
   475     if ((ERROR_EXTRA(err) = gnutls_init(&sock->session, GNUTLS_CLIENT)) < 0)
   492         JUMP_SET_ERROR(err, ERR_GNUTLS_INIT);
   476         JUMP_SET_ERROR(err, ERR_GNUTLS_INIT);
   493 
   477 
   494     // ...default priority stuff
   478     // ...default priority stuff
   501     // bind credentials
   485     // bind credentials
   502     if ((ERROR_EXTRA(err) = gnutls_credentials_set(sock->session, GNUTLS_CRD_CERTIFICATE, cred->x509)))
   486     if ((ERROR_EXTRA(err) = gnutls_credentials_set(sock->session, GNUTLS_CRD_CERTIFICATE, cred->x509)))
   503         JUMP_SET_ERROR(err, ERR_GNUTLS_CRED_SET);
   487         JUMP_SET_ERROR(err, ERR_GNUTLS_CRED_SET);
   504 
   488 
   505     // TCP connect
   489     // TCP connect
   506     if (sock_tcp_connect_async_begin(SOCK_GNUTLS_TCP(sock), hostname, service, err))
   490     if (sock_tcp_connect_async(SOCK_GNUTLS_TCP(sock), hostname, service, err))
   507         goto error;
   491         goto error;
   508 
   492 
   509     // done, wait for the connect to complete
   493     // done, wait for the connect to complete
   510     *sock_ptr = SOCK_GNUTLS_BASE(sock);
   494     *transport_ptr = SOCK_GNUTLS_TRANSPORT(sock);
   511 
   495 
   512     return SUCCESS;
   496     return SUCCESS;
   513 
   497 
   514 error:
   498 error:
   515     // cleanup
   499     // cleanup
   518     return ERROR_CODE(err);    
   502     return ERROR_CODE(err);    
   519 }
   503 }
   520 
   504 
   521 void sock_gnutls_destroy (struct sock_gnutls *sock)
   505 void sock_gnutls_destroy (struct sock_gnutls *sock)
   522 {
   506 {
   523     // terminate the TCP transport
       
   524     sock_fd_close(SOCK_GNUTLS_FD(sock));
       
   525 
       
   526     // close the session rudely
   507     // close the session rudely
   527     gnutls_deinit(sock->session);
   508     gnutls_deinit(sock->session);
   528     
   509  
       
   510     // terminate the TCP transport
       
   511     sock_tcp_destroy(SOCK_GNUTLS_TCP(sock));
       
   512    
   529     if (sock->cred)
   513     if (sock->cred)
   530         // drop the cred ref
   514         // drop the cred ref
   531         sock_ssl_client_cred_put(sock->cred);
   515         sock_ssl_client_cred_put(sock->cred);
   532 
   516 
   533     // free
   517     // free
   534     free(sock->hostname);
   518     free(sock->hostname);
   535     free(sock);
   519 }
   536 }
   520 
   537