Index: src/verify.c =================================================================== *** src/verify.c (revision 2825) --- src/verify.c (working copy) *************** *** 49,54 **** --- 49,55 ---- static int cert_check(CLI *c, X509_STORE_CTX *, char *, int); static int crl_check(CLI *c, X509_STORE_CTX *, char *); static int ocsp_check(CLI *c, X509_STORE_CTX *, char *); + static int dns_check(CLI *c, X509_STORE_CTX *, char *); /* utility functions */ static void log_time(const int, const char *, ASN1_TIME *); *************** *** 177,182 **** --- 178,185 ---- if(c->opt->option.ocsp && !ocsp_check(c, callback_ctx, subject_name)) return 0; /* reject connection */ #endif /* OpenSSL-0.9.7 */ + if(c->opt->option.verify_dns && !dns_check(c, callback_ctx, subject_name)) + return 0; /* reject connection */ /* errnum=X509_STORE_CTX_get_error(ctx); */ s_log(LOG_NOTICE, "VERIFY OK: depth=%d, %s", *************** *** 458,463 **** --- 461,573 ---- } #endif /* OpenSSL-0.9.7 */ + static int dns_docheck(X509_STORE_CTX *callback_ctx, char *subject_name, + ASN1_STRING *expected, const struct sockaddr *sa, + socklen_t salen, char *buf) { + int err; + err=getnameinfo(sa, salen, buf, expected->length + 2, NULL, 0, 0); + if(err) { + s_log(LOG_WARNING, + "DNS VERIFY ERROR: depth=%d, %s: %s", + callback_ctx->error_depth, + subject_name, + gai_strerror(err)); + /* reject connection */ + return 0; + } + if(memcmp(expected->data, buf, expected->length)!=0 || + buf[expected->length] != '\0') { + s_log(LOG_WARNING, + "DNS VERIFY ERROR: CN=%.*s, actual=%s, depth=%d, %s", + expected->length, + expected->data, + buf, + callback_ctx->error_depth, + subject_name); + /* reject connection */ + return 0; + } + return 1; + } + + static int dns_check(CLI *c, X509_STORE_CTX *callback_ctx, char *subject_name) { + X509_NAME *name; + int i; + ASN1_STRING *data; + FD *rfd; + FD *wfd; + SOCKADDR_UNION rfd_peer; + SOCKADDR_UNION wfd_peer; + socklen_t rfd_peer_size; + socklen_t wfd_peer_size; + char *buf; + int retval; + + if(callback_ctx->error_depth!=0) + /* Only the leaf cert needs its commonName checked */ + return 1; + name=X509_get_subject_name(callback_ctx->current_cert); + i=X509_NAME_get_index_by_NID(name, NID_commonName, -1); + if(i<0) { + /* No common name at all? That's too weird, let's reject */ + s_log(LOG_WARNING, + "DNS VERIFY ERROR: No commonName, depth=%d, %s", + callback_ctx->error_depth, + subject_name); + return 0; + } + data=X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name, i)); + if(c->opt->option.client) { + rfd = &c->remote_fd; + wfd = &c->remote_fd; + } else { + rfd = &c->local_rfd; + wfd = &c->local_wfd; + } + if(!rfd->is_socket || !wfd->is_socket) { + s_log(LOG_WARNING, + "DNS VERIFY ERROR: Must be using sockets, depth=%d, %s", + callback_ctx->error_depth, + subject_name); + /* reject connection. Don't set verify_dns if your set-up doesn't use + sockets! */ + return 0; + } + rfd_peer_size=sizeof rfd_peer; + wfd_peer_size=sizeof wfd_peer; + if(getpeername(rfd->fd, &rfd_peer.sa, &rfd_peer_size) < 0 || + (rfd->fd!=wfd->fd && + getpeername(wfd->fd, &wfd_peer.sa, &wfd_peer_size) < 0)) { + + s_log(LOG_WARNING, + "DNS VERIFY ERROR: Unknown peer, depth=%d, %s: %s", + callback_ctx->error_depth, + subject_name, + my_strerror(errno)); + /* reject connection */ + return 0; + } + buf=malloc(data->length + 2); + if(!buf) + /* reject connection */ + return 0; + if (!dns_docheck(callback_ctx, subject_name, data, + &rfd_peer.sa, rfd_peer_size, buf) || + (rfd->fd!=wfd->fd && + !dns_docheck(callback_ctx, subject_name, data, + &wfd_peer.sa, wfd_peer_size, buf))) { + /* reject connection */ + retval=0; + goto cleanup; + } + /* accept */ + retval=1; + + cleanup: + free(buf); + return retval; + } + static void log_time(const int level, const char *txt, ASN1_TIME *t) { char *cp; BIO *bio; Index: src/options.c =================================================================== *** src/options.c (revision 2825) --- src/options.c (working copy) *************** *** 1401,1406 **** --- 1401,1429 ---- break; } + /* verify_dns */ + switch(cmd) { + case CMD_INIT: + section->option.verify_dns=0; + break; + case CMD_EXEC: + if(strcasecmp(opt, "verify_dns")) + break; + if(!strcasecmp(arg, "yes")) + section->option.verify_dns=1; + else if(!strcasecmp(arg, "no")) + section->option.verify_dns=0; + else + return "Argument should be either 'yes' or 'no'"; + return NULL; /* OK */ + case CMD_DEFAULT: + break; + case CMD_HELP: + s_log(LOG_NOTICE, "%-15s = yes|no verify CN against DNS", + "verify_dns"); + break; + } + if(cmd==CMD_EXEC) return option_not_found; return NULL; /* OK */ Index: src/prototypes.h =================================================================== *** src/prototypes.h (revision 2825) --- src/prototypes.h (working copy) *************** *** 188,193 **** --- 188,194 ---- #ifdef USE_LIBWRAP unsigned int libwrap:1; #endif + unsigned int verify_dns:1; } option; } SERVICE_OPTIONS;