/* ** Copyright 2007-2019 Double Precision, Inc. ** See COPYING for distribution information. */ #include "config.h" #include "argparse.h" #include "spipe.h" #include "libcouriertls.h" #include "tlscache.h" #include "soxwrap/soxwrap.h" #include #ifndef HAVE_GNUTLS3 #include #endif #include #include #include #include #include #include #include #if HAVE_DIRENT_H #include #define NAMLEN(dirent) strlen((dirent)->d_name) #else #define dirent direct #define NAMLEN(dirent) (dirent)->d_namlen #if HAVE_SYS_NDIR_H #include #endif #if HAVE_SYS_DIR_H #include #endif #if HAVE_NDIR_H #include #endif #endif #if HAVE_UNISTD_H #include #endif #if HAVE_FCNTL_H #include #endif #include #if HAVE_SYS_TYPES_H #include #endif #if HAVE_SYS_STAT_H #include #endif #include #include #include struct oid_name { const char *oid; const char *name; }; static struct oid_name oid_name_list[]={ {"2.5.4.0","objectClass"}, {"2.5.4.2","knowledgeInformation"}, {"2.5.4.3","cn"}, {"2.5.4.4","sn"}, {"2.5.4.5","serialNumber"}, {"2.5.4.6","c"}, {"2.5.4.7","l"}, {"2.5.4.8","st"}, {"2.5.4.9","street"}, {"2.5.4.10","o"}, {"2.5.4.11","ou"}, {"2.5.4.12","title"}, {"2.5.4.13","description"}, {"2.5.4.14","searchGuide"}, {"2.5.4.15","businessCategory"}, {"2.5.4.16","postalAddress"}, {"2.5.4.17","postalCode"}, {"2.5.4.18","postOfficeBox"}, {"2.5.4.19","physicalDeliveryOfficeName"}, {"2.5.4.20","telephoneNumber"}, {"2.5.4.21","telexNumber"}, {"2.5.4.22","teletexTerminalIdentifier"}, {"2.5.4.23","facsimileTelephoneNumber"}, {"2.5.4.24","x121Address"}, {"2.5.4.25","internationaliSDNNumber"}, {"2.5.4.26","registeredAddress"}, {"2.5.4.27","destinationIndicator"}, {"2.5.4.28","preferredDeliveryMethod"}, {"2.5.4.29","presentationAddress"}, {"2.5.4.30","supportedApplicationContext"}, {"2.5.4.31","member"}, {"2.5.4.32","owner"}, {"2.5.4.33","roleOccupant"}, {"2.5.4.35","userPassword"}, {"2.5.4.36","userCertificate"}, {"2.5.4.37","cACertificate"}, {"2.5.4.38","authorityRevocationList"}, {"2.5.4.39","certificateRevocationList"}, {"2.5.4.40","crossCertificatePair"}, {"2.5.4.41","name"}, {"2.5.4.42","givenName"}, {"2.5.4.43","initials"}, {"2.5.4.44","generationQualifier"}, {"2.5.4.45","x500UniqueIdentifier"}, {"2.5.4.46","dnQualifier"}, {"2.5.4.47","enhancedSearchGuide"}, {"2.5.4.48","protocolInformation"}, {"2.5.4.49","distinguishedName"}, {"2.5.4.50","uniqueMember"}, {"2.5.4.51","houseIdentifier"}, {"2.5.4.52","supportedAlgorithms"}, {"2.5.4.53","deltaRevocationList"}, {"2.5.4.54","dmdName"}, {"2.5.4.65","pseudonym"}, {"0.9.2342.19200300.100.1.3","mail"}, {"0.9.2342.19200300.100.1.25","dc"}, {"0.9.2342.19200300.100.1.1","uid"}, {"1.3.6.1.1.3.1","uidObject"}, {"1.2.840.113549.1.9.1","emailaddress"}, }; struct ssl_context_t { int isserver; struct tls_info info_cpy; const char *priority_list; char *certfile; char *keyfile; char *dhfile; char *trustcerts; int verify_cert; int fail_if_no_cert; }; struct ssl_handle_t { struct tls_info info_cpy; ssl_context ctx; gnutls_anon_client_credentials_t anonclientcred; gnutls_anon_server_credentials_t anonservercred; gnutls_certificate_credentials_t xcred; gnutls_dh_params_t dhparams; int dhparams_initialized; gnutls_session_t session; gnutls_x509_privkey_t x509_key; }; static void nonsslerror(struct tls_info *info, const char *pfix) { char errmsg[256]; strcpy(errmsg, "couriertls: "); strncat(errmsg, pfix, 200); strcat(errmsg, ": "); strncat(errmsg, strerror(errno), 255 - strlen(errmsg)); (*info->tls_err_msg)(errmsg, info->app_data); } #if HAVE_GNUTLS_ALPN static void sslerror(ssl_context ctx, int errcode, const char *pfix) { char errmsg[512]; const char *err; strcat(strcpy(errmsg, pfix), ": "); err=gnutls_strerror(errcode); strncat(errmsg, err, sizeof(errmsg)-1-strlen(errmsg)); errmsg[sizeof(errmsg)-1]=0; (*ctx->info_cpy.tls_err_msg)(errmsg, ctx->info_cpy.app_data); } #endif static const char *safe_getenv(ssl_context context, const char *n, const char *def) { const char *v=(*context->info_cpy.getconfigvar) (n, context->info_cpy.app_data); if (!v) v=""; if (!*v) v=def; return (v); } static void log_2stderr( int level, const char *s) { fprintf(stderr, "%s", s); } ssl_context tls_create(int isserver, const struct tls_info *info) { static int first=1; ssl_context p=malloc(sizeof(struct ssl_context_t)); char *certfile=NULL; char debug_flag; if (!p) return NULL; memset(p, 0, sizeof(*p)); p->isserver=isserver; p->info_cpy=*info; p->info_cpy.certificate_verified=0; debug_flag=*safe_getenv(p, "TLS_DEBUG", ""); if (first) { if (gnutls_check_version(LIBGNUTLS_VERSION) == NULL) { fprintf(stderr, "GnuTLS version mismatch\n"); free(p); errno=EINVAL; return (NULL); } first=0; if (debug_flag) { gnutls_global_set_log_function(log_2stderr); gnutls_global_set_log_level(9); } if (gnutls_global_init() < 0) { fprintf(stderr, "gnutls_global_init() failed\n"); free(p); errno=EINVAL; return (NULL); } #ifndef HAVE_GNUTLS3 if (gnutls_global_init_extra() < 0) { gnutls_global_deinit(); fprintf(stderr, "gnutls_global_init() failed\n"); free(p); errno=EINVAL; return (NULL); } #endif } p->priority_list=safe_getenv(p, "TLS_PRIORITY", "NORMAL:-CTYPE-OPENPGP"); if ((certfile=strdup(safe_getenv(p, "TLS_CERTFILE", ""))) == NULL || (p->trustcerts=strdup(safe_getenv(p, "TLS_TRUSTCERTS", ""))) == NULL) { if (certfile) free(certfile); tls_destroy(p); return NULL; } if (*certfile) { p->certfile=certfile; certfile=NULL; } if (certfile) free(certfile); if ((certfile=strdup(safe_getenv(p, "TLS_DHPARAMS", ""))) != NULL && *certfile) { p->dhfile=certfile; } else { if (certfile) free(certfile); } if ((certfile=strdup(safe_getenv(p, "TLS_PRIVATE_KEYFILE", ""))) != NULL && *certfile) { p->keyfile=certfile; } else { if (certfile) free(certfile); } switch (*safe_getenv(p, "TLS_VERIFYPEER", "P")) { case 'n': case 'N': p->verify_cert=0; p->fail_if_no_cert=0; break; case 'p': case 'P': /* PEER */ p->verify_cert=1; p->fail_if_no_cert=0; break; case 'r': case 'R': /* REQUIREPEER */ p->verify_cert=1; p->fail_if_no_cert=1; break; } if (info->peer_verify_domain) p->verify_cert=p->fail_if_no_cert=1; { const char *filename=safe_getenv(p, "TLS_CACHEFILE", ""); const char *cachesize=safe_getenv(p, "TLS_CACHESIZE", ""); off_t cachesize_l; if (filename && *filename) { cachesize_l= cachesize ? (off_t)atol(cachesize):0; if (cachesize_l <= 0) cachesize_l=512L * 1024; if ((p->info_cpy.tlscache=tls_cache_open(filename, cachesize_l)) == NULL) { nonsslerror(&p->info_cpy, filename); tls_destroy(p); return NULL; } } } #if 0 int session_timeout=atoi(safe_getenv(ctx, "TLS_TIMEOUT")); #endif return p; } void tls_destroy(ssl_context p) { if (p->certfile) free(p->certfile); if (p->keyfile) free(p->keyfile); if (p->dhfile) free(p->dhfile); if (p->trustcerts) free(p->trustcerts); if (p->info_cpy.tlscache) tls_cache_close(p->info_cpy.tlscache); free(p); } int tls_certificate_verified(ssl_handle ssl) { return ssl->info_cpy.certificate_verified; } static int read_cert_dir(const char *cert_dir, int (*cb_func)(const char *filename, struct stat *stat_buf, void *arg), void *arg) { DIR *dirp; struct dirent *de; int rc=0; if ((dirp=opendir(cert_dir)) == NULL) return 0; while ((de=readdir(dirp)) != NULL) { char *buf; struct stat stat_buf; if (de->d_name[0] == '.') continue; buf=malloc(strlen(cert_dir)+strlen(de->d_name)+2); if (!buf) continue; strcat(strcat(strcpy(buf, cert_dir), "/"), de->d_name); if (stat(buf, &stat_buf) < 0 || !S_ISREG(stat_buf.st_mode)) { free(buf); continue; } rc=(*cb_func)(buf, &stat_buf, arg); free(buf); if (rc) break; } closedir(dirp); return rc; } static int cnt_cert_size(const char *filename, struct stat *stat_buf, void *arg) { *(size_t *)arg += stat_buf->st_size; return 0; } struct cert_buf_ptr { char *ptr; size_t cnt; }; static int save_cert_to_buf(const char *filename, struct stat *stat_buf, void *arg) { struct cert_buf_ptr *p=(struct cert_buf_ptr *)arg; FILE *fp; if (p->cnt < stat_buf->st_size) return 1; fp=fopen(filename, "r"); if (fp) { if (stat_buf->st_size && fread(p->ptr, stat_buf->st_size, 1, fp) != 1) { fclose(fp); return 1; } fclose(fp); } p->ptr += stat_buf->st_size; p->cnt -= stat_buf->st_size; return 0; } static int add_certificates(gnutls_certificate_credentials_t xcred, const char *certfile) { struct stat stat_buf; struct cert_buf_ptr ptr; gnutls_datum_t datum_ptr; if (!certfile || !*certfile || stat(certfile, &stat_buf) < 0) return 0; if (S_ISREG(stat_buf.st_mode)) { return gnutls_certificate_set_x509_trust_file(xcred, certfile, GNUTLS_X509_FMT_PEM); } if (!S_ISDIR(stat_buf.st_mode)) return 0; ptr.cnt=0; if (read_cert_dir(certfile, cnt_cert_size, &ptr.cnt)) return 0; datum_ptr.data=malloc(ptr.cnt+1); datum_ptr.size=ptr.cnt; if (!datum_ptr.data) return 0; ptr.ptr=(char *)datum_ptr.data; if (read_cert_dir(certfile, save_cert_to_buf, &ptr) || ptr.cnt) { free(datum_ptr.data); return 0; } *ptr.ptr=0; gnutls_certificate_set_x509_trust_mem(xcred, &datum_ptr, GNUTLS_X509_FMT_PEM); free(datum_ptr.data); return 0; } static void tls_free_session_keys(ssl_handle ssl) { if (ssl->x509_key) gnutls_x509_privkey_deinit(ssl->x509_key); ssl->x509_key=NULL; } static void tls_free_session(ssl_handle ssl) { gnutls_deinit(ssl->session); gnutls_certificate_free_credentials(ssl->xcred); gnutls_anon_free_client_credentials(ssl->anonclientcred); gnutls_anon_free_server_credentials(ssl->anonservercred); gnutls_dh_params_deinit(ssl->dhparams); tls_free_session_keys(ssl); free(ssl); } static int chk_error(int rc, ssl_handle ssl, int fd, fd_set *r, fd_set *w, int *result_rc) { if (rc == GNUTLS_E_SUCCESS) { if (result_rc) *result_rc=0; return 0; } if (rc == GNUTLS_E_WARNING_ALERT_RECEIVED) return 1; if (rc == GNUTLS_E_FATAL_ALERT_RECEIVED) { const char *alert= gnutls_alert_get_name(gnutls_alert_get(ssl->session)); (*ssl->info_cpy.tls_err_msg)(alert, ssl->info_cpy.app_data); if (result_rc) *result_rc= -1; return 0; } if (rc == GNUTLS_E_AGAIN || rc == GNUTLS_E_INTERRUPTED) { fd_set *p=gnutls_record_get_direction(ssl->session) ? w:r; if (p) FD_SET(fd, p); if (result_rc) *result_rc=1; return 0; } if (result_rc) { const char *errmsg=gnutls_strerror(rc); #ifdef GNUTLS_E_PREMATURE_TERMINATION if (rc == GNUTLS_E_PREMATURE_TERMINATION) { errmsg="DEBUG: Unexpected SSL connection shutdown."; } #endif (*ssl->info_cpy.tls_err_msg)(errmsg, ssl->info_cpy.app_data); *result_rc= -1; } return 0; } static int name_check(ssl_handle ssl, gnutls_x509_crt_t cert) { char *idn_domain; const char *p; int rc; if (idna_to_unicode_8z8z(ssl->info_cpy.peer_verify_domain, &idn_domain, 0) != IDNA_SUCCESS) idn_domain=0; p=idn_domain ? idn_domain:ssl->info_cpy.peer_verify_domain; rc=gnutls_x509_crt_check_hostname(cert, p); if (idn_domain) { free(idn_domain); /* Now try the ACE-encoded hostname for a filename. */ if (rc == 0) { p=ssl->info_cpy.peer_verify_domain; rc=gnutls_x509_crt_check_hostname(cert, p); } } return rc; } static int verify_client(ssl_handle ssl, int fd) { unsigned int status; int rc; const gnutls_datum_t *cert_list; unsigned int cert_list_size; if (!ssl->ctx->verify_cert) return 0; cert_list = gnutls_certificate_get_peers(ssl->session, &cert_list_size); if (cert_list == NULL || cert_list_size == 0) { if (ssl->ctx->fail_if_no_cert) { (*ssl->info_cpy.tls_err_msg) ("No certificate supplied by peer", ssl->info_cpy.app_data); return GNUTLS_E_INSUFFICIENT_CREDENTIALS; } return 0; } status=0; rc=gnutls_certificate_verify_peers2(ssl->session, &status); if (rc) { (*ssl->info_cpy.tls_err_msg) ("Peer certificate verification failed", ssl->info_cpy.app_data); return GNUTLS_E_INSUFFICIENT_CREDENTIALS; } if (status) { (*ssl->info_cpy.tls_err_msg) (status & GNUTLS_CERT_REVOKED ? "Peer's certificate is revoked": status & GNUTLS_CERT_SIGNER_NOT_FOUND ? "Peer's certificate not signed by a trusted authority": status & GNUTLS_CERT_SIGNER_NOT_CA ? "Invalid peer certificate authority": status & GNUTLS_CERT_INSECURE_ALGORITHM ? "Peer's certificate does not use a secure checksum": "Invalid peer certificate", ssl->info_cpy.app_data); return GNUTLS_E_INSUFFICIENT_CREDENTIALS; } if (gnutls_certificate_type_get(ssl->session) == GNUTLS_CRT_X509) { gnutls_x509_crt_t cert; if (gnutls_x509_crt_init(&cert) < 0) { (*ssl->info_cpy.tls_err_msg) ("Error initializing certificate", ssl->info_cpy.app_data); return GNUTLS_E_INSUFFICIENT_CREDENTIALS; } if (gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER) < 0) { (*ssl->info_cpy.tls_err_msg) ("Error parsing certificate", ssl->info_cpy.app_data); gnutls_x509_crt_deinit (cert); return GNUTLS_E_INSUFFICIENT_CREDENTIALS; } if (gnutls_x509_crt_get_expiration_time(cert) < time(NULL)) { (*ssl->info_cpy.tls_err_msg) ("Expired certificate", ssl->info_cpy.app_data); gnutls_x509_crt_deinit (cert); return GNUTLS_E_INSUFFICIENT_CREDENTIALS; } if (gnutls_x509_crt_get_activation_time(cert) > time(NULL)) { (*ssl->info_cpy.tls_err_msg) ("Certificate not activated", ssl->info_cpy.app_data); gnutls_x509_crt_deinit (cert); return GNUTLS_E_INSUFFICIENT_CREDENTIALS; } if (ssl->info_cpy.peer_verify_domain && *ssl->info_cpy.peer_verify_domain && !name_check(ssl, cert)) { char hostname[256]; size_t hostname_size=sizeof(hostname); const char *errmsg_txt="Certificate owner mismatch: "; char *errmsg_buf; if (gnutls_x509_crt_get_dn_by_oid(cert, "2.5.4.3", 0, 0, hostname, &hostname_size) < 0) strcpy(hostname,"(unknown)"); errmsg_buf=malloc(strlen(errmsg_txt)+ strlen(hostname)+10); if (errmsg_buf) strcat(strcpy(errmsg_buf, errmsg_txt), hostname); (*ssl->info_cpy.tls_err_msg) (errmsg_buf ? errmsg_buf: strerror(errno), ssl->info_cpy.app_data); gnutls_x509_crt_deinit (cert); if (errmsg_buf) free(errmsg_buf); return GNUTLS_E_INSUFFICIENT_CREDENTIALS; } gnutls_x509_crt_deinit (cert); } else { (*ssl->info_cpy.tls_err_msg) ("No certificate supplied by peer", ssl->info_cpy.app_data); return GNUTLS_E_INSUFFICIENT_CREDENTIALS; } ssl->info_cpy.certificate_verified=1; return 0; } #if HAVE_GNUTLS_ALPN /* ** Parse the TLS_ALPN setting, repeatedly invoking a callback with: ** 1) size of the protocol string ** 2) the protocol string ** 3) pass-through parameter. */ static gnutls_alpn_flags_t alpn_parse(const char *alpn, void (*cb)(unsigned char l, const char *p, void *arg), void *arg) { gnutls_alpn_flags_t flags=GNUTLS_ALPN_MANDATORY; while (alpn && *alpn) { const char *p; unsigned char l; /* Commas and spaces are delimiters here */ if (*alpn==',' || isspace(*alpn)) { ++alpn; continue; } /* Look for the next comma, spaces, or end of string */ p=alpn; while (*alpn && *alpn != ',' && !isspace(*alpn)) ++alpn; /* ** We now have the character count and the string. ** Defend against a bad setting by checking for overflow. */ l=alpn - p; if (l != alpn - p) continue; if (*p != '@') (*cb)(l, p, arg); else { if (l > 1) switch (p[1]) { case 'o': case 'O': flags &= GNUTLS_ALPN_MANDATORY; break; case 's': case 'S': flags |= GNUTLS_ALPN_SERVER_PRECEDENCE; break; } } } return flags; } static void alpn_count(unsigned char l, const char *p, void *arg) { ++*(unsigned *)arg; } static void alpn_save(unsigned char l, const char *name, void *arg) { gnutls_datum_t **p=(gnutls_datum_t **)arg; (*p)->data=(unsigned char *)name; (*p)->size=l; ++*p; } static int alpn_set(ssl_context ctx, gnutls_session_t session) { const char *p=safe_getenv(ctx, "TLS_ALPN", ""); unsigned n=0; gnutls_datum_t *datums; gnutls_datum_t *datum_ptr; gnutls_alpn_flags_t flags; int ret; alpn_parse(p, alpn_count, &n); if (n == 0) return GNUTLS_E_SUCCESS; if ((datums=(gnutls_datum_t *)malloc(n * sizeof(gnutls_datum_t))) == NULL) { return GNUTLS_E_SUCCESS; } datum_ptr=datums; flags=alpn_parse(p, alpn_save, &datum_ptr); ret=gnutls_alpn_set_protocols(session, datums, n, flags); free((char *)datums); return ret; } #if 0 static int verify_alpn(ssl_handle ssl) { const char *p=safe_getenv(ssl->ctx, "TLS_ALPN", ""); unsigned n=0; gnutls_datum_t protocol; int rc; alpn_parse(p, alpn_count, &n); rc=gnutls_alpn_get_selected_protocol(ssl->session, &protocol); if (rc == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { fprintf(stderr, "Not negotiated\n"); return 0; } if (rc != GNUTLS_E_SUCCESS) { fprintf(stderr, "ERROR: %d %s\n", rc, gnutls_strerror(rc)); return 0; } fprintf(stderr, "Negotiated "); fwrite(protocol.data, protocol.size, 1, stderr); fprintf(stderr, "\n"); return 0; } #endif #endif static int dohandshake(ssl_handle ssl, int fd, fd_set *r, fd_set *w) { int rc; while (chk_error(gnutls_handshake(ssl->session), ssl, fd, r, w, &rc)) ; if (rc == 0) { ssl->info_cpy.connect_interrupted=0; if (verify_client(ssl, fd)) return -1; #if 0 if (verify_alpn(ssl)) return -1; #endif if (ssl->info_cpy.connect_callback != NULL && !(*ssl->info_cpy.connect_callback)(ssl, ssl->info_cpy.app_data)) return (-1); } return rc; } static char *check_cert_unicode(const char *filename, gnutls_certificate_type_t cert_type, const char *req_dn, int isvirtual) { if (!filename || !*filename) return NULL; while (*req_dn) { char *p=malloc(strlen(filename)+strlen(req_dn)+10); if (!p) return NULL; strcat(strcat(strcpy(p, filename), "."), req_dn); if (access(p, R_OK) == 0) return p; free(p); if (!isvirtual) break; if ((req_dn=strchr(req_dn, '.')) == NULL) break; ++req_dn; } /* ** We're called with a hostname first. Don't check the defualt ** filename, we'll be called again with an IP address */ if (!isvirtual) { char *p=malloc(strlen(filename)+10); if (!p) return NULL; strcpy(p, filename); if (access(p, R_OK) == 0) return p; free(p); } return NULL; } static char *check_cert(const char *filename, gnutls_certificate_type_t cert_type, const char *req_dn, int isvirtual) { if (isvirtual) { char *p; char *retfile; if (idna_to_ascii_8z(req_dn, &p, 0) != IDNA_SUCCESS) p=0; if (p) { retfile=check_cert_unicode(filename, cert_type, p, isvirtual); free(p); if (retfile) return retfile; } } return check_cert_unicode(filename, cert_type, req_dn, isvirtual); } static char *check_key(const char *filename, gnutls_certificate_type_t cert_type, const char *req_dn, int isvirtual) { return check_cert(filename, cert_type, req_dn, isvirtual); } static int read_file(const char *file, gnutls_datum_t *filebuf) { FILE *fp; struct stat stat_buf; filebuf->data=NULL; if ((fp=fopen(file, "r")) == NULL || fstat(fileno(fp), &stat_buf) < 0) { if (fp) fclose(fp); return GNUTLS_E_FILE_ERROR; } if ((filebuf->data=malloc(stat_buf.st_size)) == NULL) { fclose(fp); return GNUTLS_E_MEMORY_ERROR; } if (fread(filebuf->data, filebuf->size=stat_buf.st_size, 1, fp) != 1) { if (fp) fclose(fp); return GNUTLS_E_FILE_ERROR; } return 0; } static void release_file(gnutls_datum_t *filebuf) { if (filebuf->data) free(filebuf->data); filebuf->data=NULL; } static int set_cert(ssl_handle ssl, gnutls_session_t session, gnutls_retr2_st *st, const char *certfilename, const char *keyfilename) { int rc; gnutls_datum_t filebuf; gnutls_datum_t keyfilebuf; unsigned int cert_cnt; st->ncerts=0; st->deinit_all=0; tls_free_session_keys(ssl); if ((rc=read_file(certfilename, &filebuf)) < 0) return rc; if (keyfilename) { if ((rc=read_file(keyfilename, &keyfilebuf)) < 0) { release_file(&filebuf); return rc; } } switch (st->cert_type) { case GNUTLS_CRT_X509: cert_cnt=0; if (keyfilename) { if ((rc=gnutls_x509_privkey_init(&ssl->x509_key)) < 0 || (rc=gnutls_x509_privkey_import(ssl->x509_key, &keyfilebuf, GNUTLS_X509_FMT_PEM)) < 0) break; } else { if ((rc=gnutls_x509_privkey_init(&ssl->x509_key)) < 0 || (rc=gnutls_x509_privkey_import(ssl->x509_key, &filebuf, GNUTLS_X509_FMT_PEM)) < 0) break; } rc=gnutls_x509_crt_list_import(NULL, &cert_cnt, &filebuf, GNUTLS_X509_FMT_PEM, GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED); if (rc != GNUTLS_E_SHORT_MEMORY_BUFFER) break; st->ncerts=cert_cnt+1; st->cert.x509=gnutls_malloc(st->ncerts*sizeof(*st->cert.x509)); rc=gnutls_x509_crt_list_import(st->cert.x509, &st->ncerts, &filebuf, GNUTLS_X509_FMT_PEM, 0); if (rc < 0) { st->ncerts=0; gnutls_free(st->cert.x509); st->cert.x509=0; break; } st->ncerts=rc; st->key.x509=ssl->x509_key; ssl->x509_key=0; st->deinit_all=1; break; default: break; } release_file(&filebuf); if (keyfilename) release_file(&keyfilebuf); return 0; } static int get_server_cert(gnutls_session_t session, const gnutls_datum_t * req_ca_rdn, int nreqs, const gnutls_pk_algorithm_t * sign_algos, int sign_algos_length, gnutls_retr2_st *st) { ssl_handle ssl=(ssl_handle)gnutls_session_get_ptr(session); int vhost_idx; char *vhost_buf; size_t vhost_max_size=0; size_t vhost_size; unsigned int type=GNUTLS_NAME_DNS; char *certfilename=NULL; char *keyfilename=NULL; int rc; st->cert_type=gnutls_certificate_type_get(session); for (vhost_idx=0; vhost_size=0, gnutls_server_name_get(session, NULL, &vhost_size, &type, vhost_idx) == GNUTLS_E_SHORT_MEMORY_BUFFER; ++vhost_idx) { if (++vhost_size > vhost_max_size) vhost_max_size=vhost_size; } vhost_buf=malloc(vhost_max_size); if (!vhost_buf) return GNUTLS_E_MEMORY_ERROR; for (vhost_idx=0; vhost_size=vhost_max_size, gnutls_server_name_get(session, vhost_buf, &vhost_size, &type, vhost_idx) == GNUTLS_E_SUCCESS; ++vhost_idx) { char *p; char *utf8; char *namebuf; /* Convert to UTF8 */ if (idna_to_unicode_8z8z(vhost_buf, &utf8, 0) != IDNA_SUCCESS) utf8=0; namebuf=utf8 ? utf8:vhost_buf; for (p=namebuf; *p; p++) if (*p == '/') *p='.'; /* Script kiddie check */ if (ssl->ctx->certfile) certfilename=check_cert(ssl->ctx->certfile, st->cert_type, namebuf, 1); if (ssl->ctx->keyfile) keyfilename=check_key(ssl->ctx->keyfile, st->cert_type, namebuf, 1); if (utf8) free(utf8); if (certfilename) break; } if (!certfilename) { const char *ip= safe_getenv(ssl->ctx, "TCPLOCALIP", ""); if (strncmp(ip, "::ffff:", 7) == 0 && strchr(ip, '.')) ip += 7; if (ssl->ctx->certfile) certfilename=check_cert(ssl->ctx->certfile, st->cert_type, ip, 0); if (ssl->ctx->keyfile) keyfilename=check_key(ssl->ctx->keyfile, st->cert_type, ip, 0); } if (!certfilename) { if (ssl->ctx->certfile) nonsslerror(&ssl->info_cpy, ssl->ctx->certfile); free(vhost_buf); return 0; } rc=set_cert(ssl, session, st, certfilename, keyfilename); free(certfilename); if (keyfilename) free(keyfilename); free(vhost_buf); return rc; } static int pick_client_cert(gnutls_session_t session, const gnutls_datum_t * req_ca_rdn, int nreqs, const gnutls_pk_algorithm_t * sign_algos, int sign_algos_length, gnutls_retr2_st *st) { ssl_handle ssl=(ssl_handle)gnutls_session_get_ptr(session); int i, j; const char *cert_array; size_t cert_array_size; int rc=0; if (ssl->info_cpy.getpemclientcert4ca == NULL) return 0; if (st->cert_type != GNUTLS_CRT_X509) return 0; if (ssl->info_cpy.loadpemclientcert4ca) (*ssl->info_cpy.loadpemclientcert4ca)(ssl->info_cpy.app_data); for (j=0; (*ssl->info_cpy.getpemclientcert4ca)(j, &cert_array, &cert_array_size, ssl->info_cpy.app_data); ++j) { gnutls_datum_t data; unsigned int cert_cnt=0; gnutls_x509_crt_t *certbuf; size_t issuer_buf_size=0; char *issuer_rdn; gnutls_x509_privkey_t pk; data.data=(unsigned char *)cert_array; data.size=cert_array_size; gnutls_x509_privkey_init(&pk); if (gnutls_x509_privkey_import(pk, &data, GNUTLS_X509_FMT_PEM) != GNUTLS_E_SUCCESS) { gnutls_x509_privkey_deinit(pk); continue; } data.data=(void *)cert_array; data.size=cert_array_size;; gnutls_x509_crt_list_import(NULL, &cert_cnt, &data, GNUTLS_X509_FMT_PEM, GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED); if (cert_cnt == 0) { gnutls_x509_privkey_deinit(pk); continue; } certbuf=gnutls_malloc(sizeof(*certbuf)*cert_cnt); if (!certbuf) { gnutls_x509_privkey_deinit(pk); continue; } if (gnutls_x509_crt_list_import(certbuf, &cert_cnt, &data, GNUTLS_X509_FMT_PEM, GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED) < 0) { free(certbuf); gnutls_x509_privkey_deinit(pk); continue; } gnutls_x509_crt_get_issuer_dn(certbuf[0], NULL, &issuer_buf_size); ++issuer_buf_size; issuer_rdn=gnutls_malloc(issuer_buf_size+1); if (gnutls_x509_crt_get_issuer_dn(certbuf[0], issuer_rdn, &issuer_buf_size) != GNUTLS_E_SUCCESS) { gnutls_free(issuer_rdn); issuer_rdn=0; } else issuer_rdn[issuer_buf_size]=0; for (i=0; issuer_rdn && incerts=0; if (issuer_rdn && i < nreqs) { st->cert.x509=certbuf; st->ncerts=cert_cnt; st->deinit_all=1; st->key.x509=pk; cert_cnt=0; rc=1; } else { gnutls_x509_privkey_deinit(pk); while (cert_cnt) gnutls_x509_crt_deinit(certbuf[--cert_cnt]); gnutls_free(certbuf); } gnutls_free(issuer_rdn); if (rc) break; } return rc; } static int get_client_cert(gnutls_session_t session, const gnutls_datum_t * req_ca_rdn, int nreqs, const gnutls_pk_algorithm_t * sign_algos, int sign_algos_length, gnutls_retr2_st *st) { ssl_handle ssl=(ssl_handle)gnutls_session_get_ptr(session); int rc; char *certfilename=NULL; char *keyfilename=NULL; rc= 0; st->cert_type=gnutls_certificate_type_get(session); if (ssl->ctx->certfile) certfilename=check_cert(ssl->ctx->certfile, st->cert_type, "", 0); if (ssl->ctx->keyfile) keyfilename=check_key(ssl->ctx->keyfile, st->cert_type, "", 0); st->ncerts=0; st->deinit_all=0; if (certfilename) { rc=set_cert(ssl, session, st, certfilename, keyfilename); free(certfilename); } else { rc=pick_client_cert(session, req_ca_rdn, nreqs, sign_algos, sign_algos_length, st); if (rc > 0) rc=0; } if (keyfilename) free(keyfilename); return rc; } static int read_dh_params(gnutls_dh_params_t dhparams, const char *filename, int *dhparams_initialized) { int rc; gnutls_datum_t filebuf; if (*dhparams_initialized) return 0; if (!filename) return 0; rc=read_file(filename, &filebuf); if (rc == 0) { if (gnutls_dh_params_import_pkcs3(dhparams, &filebuf, GNUTLS_X509_FMT_PEM) == 0) *dhparams_initialized=1; release_file(&filebuf); } return 0; } static int db_store_func(void *dummy, gnutls_datum_t key, gnutls_datum_t data) { char *p=malloc(key.size + data.size + sizeof(int)); if (!p) return -1; memcpy(p, &key.size, sizeof(key.size)); memcpy(p+sizeof(key.size), key.data, key.size); memcpy(p+sizeof(key.size)+key.size, data.data, data.size); tls_cache_add(((ssl_handle)dummy)->info_cpy.tlscache, p, key.size + data.size + sizeof(int)); free(p); return 0; } static int do_cache_remove(void *rec, size_t recsize, int *doupdate, void *arg) { char *recptr=rec; gnutls_datum_t *key=(gnutls_datum_t *)arg; if (recsize >= key->size + sizeof(key->size)) { gnutls_datum_t dummy; memcpy(&dummy.size, recptr, sizeof(dummy.size)); if (dummy.size == key->size && memcmp(recptr + sizeof(key->size), key->data, key->size) == 0) { dummy.size= -1; memcpy(recptr, &dummy.size, sizeof(dummy.size)); *doupdate=1; return 1; } } return 0; } static int db_remove_func(void *dummy, gnutls_datum_t key) { tls_cache_walk(((ssl_handle)dummy)->info_cpy.tlscache, do_cache_remove, &key); return 0; } struct db_retrieve_s { gnutls_datum_t ret; gnutls_datum_t *key; }; static int do_cache_retrieve(void *rec, size_t recsize, int *doupdate, void *arg) { char *recptr=rec; struct db_retrieve_s *ret=(struct db_retrieve_s *)arg; if (recsize >= ret->key->size + sizeof(ret->key->size)) { gnutls_datum_t dummy; memcpy(&dummy.size, recptr, sizeof(dummy.size)); if (dummy.size == ret->key->size && memcmp(recptr+sizeof(dummy.size), ret->key->data, ret->key->size) == 0) { ret->ret.size=recsize-sizeof(dummy.size)-ret->key->size; ret->ret.data=gnutls_malloc(ret->ret.size); if (ret->ret.data) memcpy(ret->ret.data, (void *)(recptr+sizeof(dummy.size) +ret->key->size), ret->ret.size); else ret->ret.size=0; return 1; } } return 0; } static gnutls_datum_t db_retrieve_func(void *dummy, gnutls_datum_t key) { struct db_retrieve_s drs; drs.ret.data=NULL; drs.ret.size=0; drs.key= &key; tls_cache_walk(((ssl_handle)dummy)->info_cpy.tlscache, do_cache_retrieve, &drs); return drs.ret; } static int name_set(ssl_handle ssl, ssl_context ctx) { char *idn_domain; const char *p; int rc; if (idna_to_unicode_8z8z(ctx->info_cpy.peer_verify_domain, &idn_domain, 0) != IDNA_SUCCESS) idn_domain=0; p=idn_domain ? idn_domain:ctx->info_cpy.peer_verify_domain; rc=gnutls_server_name_set(ssl->session, GNUTLS_NAME_DNS, p, strlen(p)); if (idn_domain) free(idn_domain); return rc; } ssl_handle tls_connect(ssl_context ctx, int fd) { ssl_handle ssl=malloc(sizeof(struct ssl_handle_t)); if (!ssl) return NULL; memset(ssl, 0, sizeof(*ssl)); ssl->info_cpy=ctx->info_cpy; ssl->ctx=ctx; if (ctx->info_cpy.peer_verify_domain && !*ctx->trustcerts) { errno=ENOENT; (*ctx->info_cpy.tls_err_msg)( "TLS_TRUSTCERTS not set", ctx->info_cpy.app_data); free(ssl); return NULL; } if (fcntl(fd, F_SETFL, O_NONBLOCK)) { nonsslerror(&ctx->info_cpy, "fcntl"); return (NULL); } #ifdef SO_KEEPALIVE { int dummy; dummy=1; if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (const char *)&dummy, sizeof(dummy)) < 0) { nonsslerror(&ctx->info_cpy, "setsockopt"); return (NULL); } } #endif #ifdef SO_LINGER { struct linger l; l.l_onoff=0; l.l_linger=0; if (setsockopt(fd, SOL_SOCKET, SO_LINGER, (const char *)&l, sizeof(l)) < 0) { nonsslerror(&ctx->info_cpy, "setsockopt"); return (NULL); } } #endif if (gnutls_anon_allocate_client_credentials(&ssl->anonclientcred) < 0) { free(ssl); return NULL; } if (gnutls_anon_allocate_server_credentials(&ssl->anonservercred) < 0) { gnutls_anon_free_client_credentials(ssl->anonclientcred); free(ssl); return NULL; } if (gnutls_certificate_allocate_credentials(&ssl->xcred) < 0) { gnutls_anon_free_server_credentials(ssl->anonservercred); gnutls_anon_free_client_credentials(ssl->anonclientcred); free(ssl); return NULL; } if (gnutls_dh_params_init(&ssl->dhparams) < 0) { gnutls_certificate_free_credentials(ssl->xcred); gnutls_anon_free_server_credentials(ssl->anonservercred); gnutls_anon_free_client_credentials(ssl->anonclientcred); free(ssl); return NULL; } if (gnutls_init (&ssl->session, ctx->isserver ? GNUTLS_SERVER:GNUTLS_CLIENT) < 0) { gnutls_certificate_free_credentials(ssl->xcred); gnutls_anon_free_server_credentials(ssl->anonservercred); gnutls_anon_free_client_credentials(ssl->anonclientcred); free(ssl); return NULL; } { const char *p=getenv("TLS_MIN_DH_BITS"); unsigned int n=atoi(p ? p:"0"); if (n) gnutls_dh_set_prime_bits(ssl->session, n); } gnutls_session_set_ptr(ssl->session, ssl); gnutls_handshake_set_private_extensions(ssl->session, 1); gnutls_certificate_set_verify_flags(ssl->xcred, GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT | /* GNUTLS_VERIFY_DO_NOT_ALLOW_SAME | GNUTLS_VERIFY_ALLOW_ANY_X509_V1_CA_C RT | GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD2 | GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5 |*/ 0); gnutls_certificate_set_verify_limits(ssl->xcred, 16384, 10); ssl->dhparams_initialized=0; if (gnutls_priority_set_direct(ssl->session, ctx->priority_list, NULL) < 0 || read_dh_params(ssl->dhparams, ctx->dhfile, &ssl->dhparams_initialized) < 0 || read_dh_params(ssl->dhparams, ctx->certfile, &ssl->dhparams_initialized) < 0 || add_certificates(ssl->xcred, ctx->trustcerts) < 0 || gnutls_credentials_set(ssl->session, GNUTLS_CRD_ANON, ctx->isserver ? (void *)ssl->anonservercred : (void *)ssl->anonclientcred) < 0 || gnutls_credentials_set(ssl->session, GNUTLS_CRD_CERTIFICATE, ssl->xcred) < 0 || (ctx->info_cpy.peer_verify_domain && name_set(ssl, ctx) < 0) ) { tls_free_session(ssl); return NULL; } #if HAVE_GNUTLS_ALPN { int result_rc; result_rc=alpn_set(ctx, ssl->session); if (result_rc != GNUTLS_E_SUCCESS) { sslerror(ctx, result_rc, "gnutls_alpn_set_protocols"); tls_free_session(ssl); return (NULL); } } #endif if (ssl->dhparams_initialized) { gnutls_certificate_set_dh_params(ssl->xcred, ssl->dhparams); gnutls_anon_set_server_dh_params(ssl->anonservercred, ssl->dhparams); } if (ctx->isserver) { if (ctx->verify_cert) gnutls_certificate_server_set_request(ssl->session, ctx->fail_if_no_cert ? GNUTLS_CERT_REQUIRE: GNUTLS_CERT_REQUEST); gnutls_certificate_set_retrieve_function(ssl->xcred, get_server_cert); } else gnutls_certificate_set_retrieve_function(ssl->xcred, get_client_cert); gnutls_transport_set_ptr(ssl->session,(gnutls_transport_ptr_t) GNUTLS_CAST_PTR_T fd); if (ssl->ctx->info_cpy.tlscache) { gnutls_db_set_ptr(ssl->session, ssl); gnutls_db_set_cache_expiration(ssl->session, 3600); gnutls_db_set_remove_function(ssl->session, db_remove_func); gnutls_db_set_retrieve_function(ssl->session, db_retrieve_func); gnutls_db_set_store_function(ssl->session, db_store_func); } ssl->info_cpy.connect_interrupted=1; if (dohandshake(ssl, fd, NULL, NULL) < 0) { tls_disconnect(ssl, fd); return NULL; } return ssl; } void tls_disconnect(ssl_handle ssl, int fd) { fcntl(fd, F_SETFL, 0); gnutls_bye(ssl->session, GNUTLS_SHUT_RDWR); tls_free_session(ssl); } int tls_transfer(struct tls_transfer_info *t, ssl_handle ssl, int fd, fd_set *r, fd_set *w) { if (ssl->info_cpy.connect_interrupted) { if (dohandshake(ssl, fd, r, w) < 0) return -1; return 0; } if (t->shutdown) return -1; if (t->shutdown_interrupted) { while (chk_error(gnutls_bye(ssl->session, GNUTLS_SHUT_RDWR), ssl, fd, r, w, NULL)) ; if ((r && FD_ISSET(fd, r)) || (w && FD_ISSET(fd, w))) { return 1; } t->shutdown_interrupted=0; t->shutdown= -1; return -1; } if(0) printf("readleft=%d writeleft=%d\nread_interrupted=%d read_interrupted=%d\n", (int)t->readleft,(int)t->writeleft, t->read_interrupted,t->write_interrupted); if (!t->write_interrupted && t->readleft > 0 && t->writeleft == 0) { int rc; ssize_t n; do { n=gnutls_record_recv(ssl->session, t->readptr, t->readleft); if (n >= 0) { if (n == 0) { t->shutdown=1; return -1; } t->readptr += n; t->readleft -= n; return 0; } if ((int)n == GNUTLS_E_REHANDSHAKE) { ssl->info_cpy.connect_interrupted=1; return tls_transfer(t, ssl, fd, r, w); } } while (chk_error((int)n, ssl, fd, r, w, &rc)); if (rc < 0) { t->shutdown_interrupted=1; return tls_transfer(t, ssl, fd, r, w); } } else if (t->writeleft > 0) { int rc; ssize_t n; t->write_interrupted=0; do { n=gnutls_record_send(ssl->session, (void *)t->writeptr, t->writeleft); if (n >= 0) { if (n == 0) { t->shutdown=1; return -1; } t->writeptr += n; t->writeleft -= n; return 0; } if ((int)n == GNUTLS_E_REHANDSHAKE) { t->write_interrupted=1; ssl->info_cpy.connect_interrupted=1; return tls_transfer(t, ssl, fd, r, w); } } while (chk_error((int)n, ssl, fd, r, w, &rc)); if (rc < 0) { t->shutdown=1; return -1; } t->write_interrupted=1; } else { FD_SET(fd, r); FD_SET(fd, w); } return (1); } int tls_connecting(ssl_handle ssl) { return ssl->info_cpy.connect_interrupted; } static const char *dump_dn(gnutls_x509_crt_t cert, int (*get_dn_func)(gnutls_x509_crt_t cert, unsigned indx, void *oid, size_t * sizeof_oid), int (*get_dnval_func)(gnutls_x509_crt_t cert, const char *oid, unsigned indx, unsigned int raw_flag, void *buf, size_t *sizeof_buf), void (*dump_func)(const char *, int cnt, void *), void *dump_arg) { int idx; size_t bufsiz; size_t maxnamesize; size_t maxvalsize; char *oidname; char *oidval; int oidcnt; maxnamesize=0; maxvalsize=0; oidcnt=0; while (bufsiz=0, (*get_dn_func)(cert, oidcnt, NULL, &bufsiz) == GNUTLS_E_SHORT_MEMORY_BUFFER) { if (bufsiz > maxnamesize) maxnamesize=bufsiz; ++oidcnt; } oidname=malloc(maxnamesize); if (!oidname) return strerror(errno); for (idx=0; idx maxvalsize) maxvalsize=bufsiz; ++vidx; } } oidval=malloc(maxvalsize); if (!oidval) { free(oidname); return strerror(errno); } for (idx=0; idx= 0) { (*dump_func)(" ", -1, dump_arg); (*dump_func)(oidname_str, -1, dump_arg); (*dump_func)("=", -1, dump_arg); (*dump_func)(oidval, -1, dump_arg); (*dump_func)("\n", -1, dump_arg); ++vidx; } } free(oidval); free(oidname); return NULL; } static void print_time(const char *name, time_t t, void (*dump_func)(const char *, int cnt, void *), void *dump_arg) { struct tm *tmptr=gmtime(&t); char buf[256]; strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", tmptr); (*dump_func)(name, -1, dump_arg); (*dump_func)(": ", 2, dump_arg); (*dump_func)(buf, -1, dump_arg); (*dump_func)("\n", 1, dump_arg); } static void tls_dump_connection_info_x509(ssl_handle ssl, int server, void (*dump_func)(const char *, int cnt, void *), void *dump_arg); static void dump_cipher_name(gnutls_session_t session, void (*dump_func)(const char *, int cnt, void *), void *dump_arg); void tls_dump_connection_info(ssl_handle ssl, int server, void (*dump_func)(const char *, int cnt, void *), void *dump_arg) { if (gnutls_certificate_type_get (ssl->session) == GNUTLS_CRT_X509) tls_dump_connection_info_x509(ssl, server, dump_func, dump_arg); (*dump_func)("Version: ", -1, dump_arg); (*dump_func) (gnutls_protocol_get_name(gnutls_protocol_get_version(ssl->session)), -1, dump_arg); (*dump_func)("\n", 1, dump_arg); { char buf[10]; (*dump_func)("Bits: ", -1, dump_arg); snprintf(buf, sizeof(buf), "%d", (int) gnutls_cipher_get_key_size(gnutls_cipher_get(ssl->session)) *8); buf[sizeof(buf)-1]=0; (*dump_func)(buf, -1, dump_arg); (*dump_func)("\n", 1, dump_arg); } (*dump_func)("Cipher: ", -1, dump_arg); dump_cipher_name(ssl->session, dump_func, dump_arg); (*dump_func)("\n", 1, dump_arg); } static void dump_cipher_name(gnutls_session_t session, void (*dump_func)(const char *, int cnt, void *), void *dump_arg) { gnutls_kx_algorithm_t kx_algo; gnutls_cipher_algorithm_t cipher_algo; gnutls_mac_algorithm_t mac_algo; const char *cipher_name; kx_algo=gnutls_kx_get(session); cipher_algo=gnutls_cipher_get(session); mac_algo=gnutls_mac_get(session); cipher_name=gnutls_cipher_suite_get_name(kx_algo, cipher_algo, mac_algo); if (cipher_name) (*dump_func)(cipher_name, -1, dump_arg); else { (*dump_func)(gnutls_kx_get_name(kx_algo), -1, dump_arg); (*dump_func)("-", 1, dump_arg); (*dump_func)(gnutls_certificate_type_get_name(gnutls_certificate_type_get(session)), -1, dump_arg); (*dump_func)("-", 1, dump_arg); (*dump_func)(gnutls_cipher_get_name(cipher_algo), -1, dump_arg); (*dump_func)("-", 1, dump_arg); (*dump_func)(gnutls_mac_get_name(gnutls_mac_get(session)), -1, dump_arg); } } static void tls_dump_connection_info_x509(ssl_handle ssl, int server, void (*dump_func)(const char *, int cnt, void *), void *dump_arg) { const gnutls_datum_t *cert_list; unsigned int cert_list_size; gnutls_x509_crt_t *cert; cert_list=gnutls_certificate_get_peers(ssl->session, &cert_list_size); if (cert_list) { unsigned int i; cert=malloc(sizeof (*cert) * cert_list_size); for (i = 0; isession, cnt_desc_size, &n); buf=malloc(n); if (buf) { char *ptr=buf; gen_encryption_desc(ssl->session, save_desc, &ptr); *ptr=0; } return buf; } static void gen_encryption_desc(gnutls_session_t session, void (*dump_func)(const char *, int cnt, void *), void *dump_arg) { char buf[10]; (*dump_func)(gnutls_protocol_get_name(gnutls_protocol_get_version(session)), -1, dump_arg); (*dump_func)(",", 1, dump_arg); snprintf(buf, sizeof(buf), "%d", (int)gnutls_cipher_get_key_size(gnutls_cipher_get(session)) *8); buf[sizeof(buf)-1]=0; (*dump_func)(buf, -1, dump_arg); (*dump_func)("bits,", -1, dump_arg); dump_cipher_name(session, dump_func, dump_arg); } /* ------------------- */ int tls_validate_pem_cert(const char *buf, size_t buf_size) { gnutls_datum_t dat; unsigned int cert_cnt=0; gnutls_x509_crt_t *certbuf; dat.data=(void *)buf; dat.size=buf_size; gnutls_x509_crt_list_import(NULL, &cert_cnt, &dat, GNUTLS_X509_FMT_PEM, GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED); if (cert_cnt == 0) return 0; certbuf=malloc(sizeof(*certbuf)*cert_cnt); if (!certbuf) return 0; if (gnutls_x509_crt_list_import(certbuf, &cert_cnt, &dat, GNUTLS_X509_FMT_PEM, GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED) < 0) return 0; while (cert_cnt) gnutls_x509_crt_deinit(certbuf[--cert_cnt]); free(certbuf); return (1); } char *tls_cert_name(const char *buf, size_t buf_size) { gnutls_datum_t dat; unsigned int cert_cnt=0; gnutls_x509_crt_t *certbuf; char *p=0; size_t p_size; dat.data=(void *)buf; dat.size=buf_size; gnutls_x509_crt_list_import(NULL, &cert_cnt, &dat, GNUTLS_X509_FMT_PEM, GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED); if (cert_cnt == 0) return 0; certbuf=malloc(sizeof(*certbuf)*cert_cnt); if (!certbuf) return 0; if (gnutls_x509_crt_list_import(certbuf, &cert_cnt, &dat, GNUTLS_X509_FMT_PEM, GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED) < 0) return 0; p_size=0; gnutls_x509_crt_get_dn(certbuf[0], NULL, &p_size); ++p_size; p=malloc(p_size+1); if (p) { if (gnutls_x509_crt_get_dn(certbuf[0], p, &p_size) != GNUTLS_E_SUCCESS) { free(p); p=0; } else p[p_size]=0; } while (cert_cnt) gnutls_x509_crt_deinit(certbuf[--cert_cnt]); free(certbuf); return p; }