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;
  
