示例#1
0
/* based on verify_extract_name from tls_client.c in postfix */
static gboolean irssi_ssl_verify_hostname(X509 *cert, const char *hostname)
{
	int gen_index, gen_count;
	gboolean matched = FALSE, has_dns_name = FALSE;
	const char *cert_dns_name;
	char *cert_subject_cn;
	const GENERAL_NAME *gn;
	STACK_OF(GENERAL_NAME) * gens;
	GString *alt_names;

	alt_names = g_string_new("");

	/* Verify the dNSName(s) in the peer certificate against the hostname. */
	gens = X509_get_ext_d2i(cert, NID_subject_alt_name, 0, 0);
	if (gens) {
		gen_count = sk_GENERAL_NAME_num(gens);
		for (gen_index = 0; gen_index < gen_count && !matched; ++gen_index) {
			gn = sk_GENERAL_NAME_value(gens, gen_index);
			if (gn->type != GEN_DNS)
				continue;

			/* Even if we have an invalid DNS name, we still ultimately
			   ignore the CommonName, because subjectAltName:DNS is
			   present (though malformed). */
			has_dns_name = TRUE;
			cert_dns_name = tls_dns_name(gn);
			if (cert_dns_name && *cert_dns_name) {
				g_string_append_printf(alt_names, " '%s'", cert_dns_name);
				matched = match_hostname(cert_dns_name, hostname);
			}
    	}

	    /* Free stack *and* member GENERAL_NAME objects */
	    sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free);
	}

	if (has_dns_name) {
		if (! matched) {
			/* The CommonName in the issuer DN is obsolete when SubjectAltName is available. */
			g_warning("None of the Subject Alt Names%s in the certificate match hostname '%s'", alt_names->str, hostname);
		}
		g_string_free(alt_names, TRUE);
		return matched;
	} else { /* No subjectAltNames, look at CommonName */
		g_string_free(alt_names, TRUE);
		cert_subject_cn = tls_text_name(X509_get_subject_name(cert), NID_commonName);
	    if (cert_subject_cn && *cert_subject_cn) {
	    	matched = match_hostname(cert_subject_cn, hostname);
	    	if (! matched) {
				g_warning("SSL certificate common name '%s' doesn't match host name '%s'", cert_subject_cn, hostname);
	    	}
	    } else {
	    	g_warning("No subjectAltNames and no valid common name in certificate");
	    }
	    free(cert_subject_cn);
	}

	return matched;
}
示例#2
0
/*
 * returns 0 if we get a negative match for the hostname or the ip
 * or if we get no match at all.  returns 1 otherwise.
 */
int
match_host_and_ip(const char *host, const char *ipaddr,
    const char *patterns)
{
	int mhost, mip;

	/* negative ipaddr match */
	if ((mip = match_hostname(ipaddr, patterns, strlen(patterns))) == -1)
		return 0;
	/* negative hostname match */
	if ((mhost = match_hostname(host, patterns, strlen(patterns))) == -1)
		return 0;
	/* no match at all */
	if (mhost == 0 && mip == 0)
		return 0;
	return 1;
}
示例#3
0
文件: tls_mosq.c 项目: yashh/MQTTKit
/* This code is based heavily on the example provided in "Secure Programming
 * Cookbook for C and C++".
 */
int _mosquitto_verify_certificate_hostname(X509 *cert, const char *hostname)
{
	int i;
	char name[256];
	X509_NAME *subj;
	bool have_san_dns = false;
	STACK_OF(GENERAL_NAME) *san;
	const GENERAL_NAME *nval;
	const unsigned char *data;
	unsigned char ipv6_addr[16];
	unsigned char ipv4_addr[4];
	int ipv6_ok;
	int ipv4_ok;

#ifdef WIN32
	ipv6_ok = InetPton(AF_INET6, hostname, &ipv6_addr);
	ipv4_ok = InetPton(AF_INET, hostname, &ipv4_addr);
#else
	ipv6_ok = inet_pton(AF_INET6, hostname, &ipv6_addr);
	ipv4_ok = inet_pton(AF_INET, hostname, &ipv4_addr);
#endif

	san = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
	if(san){
		for(i=0; i<sk_GENERAL_NAME_num(san); i++){
			nval = sk_GENERAL_NAME_value(san, i);
			if(nval->type == GEN_DNS){
				data = ASN1_STRING_data(nval->d.dNSName);
				if(data && match_hostname((char *)data, hostname)){
					return 1;
				}
				have_san_dns = true;
			}else if(nval->type == GEN_IPADD){
				data = ASN1_STRING_data(nval->d.iPAddress);
				if(nval->d.iPAddress->length == 4 && ipv4_ok){
					if(!memcmp(ipv4_addr, data, 4)){
						return 1;
					}
				}else if(nval->d.iPAddress->length == 16 && ipv6_ok){
					if(!memcmp(ipv6_addr, data, 16)){
						return 1;
					}
				}
			}
		}
		if(have_san_dns){
			/* Only check CN if subjectAltName DNS entry does not exist. */
			return 0;
		}
	}
	subj = X509_get_subject_name(cert);
	if(X509_NAME_get_text_by_NID(subj, NID_commonName, name, sizeof(name)) > 0){
		name[sizeof(name) - 1] = '\0';
		if (!strcasecmp(name, hostname)) return 1;
	}
	return 0;
}
示例#4
0
static int
match_maybe_hashed(const char *host, const char *names, int *was_hashed)
{
	int hashed = *names == HASH_DELIM;
	const char *hashed_host;
	size_t nlen = strlen(names);

	if (was_hashed != NULL)
		*was_hashed = hashed;
	if (hashed) {
		if ((hashed_host = host_hash(host, names, nlen)) == NULL)
			return -1;
		return nlen == strlen(hashed_host) &&
		    strncmp(hashed_host, names, nlen) == 0;
	}
	return match_hostname(host, names) == 1;
}
示例#5
0
static int access_match(const char *list, const char *addr, const char **host_ptr)
{
	char *tok;
	char *list2 = strdup(list);

	if (!list2)
		out_of_memory("access_match");

	strlower(list2);

	for (tok = strtok(list2, " ,\t"); tok; tok = strtok(NULL, " ,\t")) {
		if (match_hostname(host_ptr, addr, tok) || match_address(addr, tok)) {
			free(list2);
			return 1;
		}
	}

	free(list2);
	return 0;
}
示例#6
0
文件: match.c 项目: mpitzl/libopenssh
/*
 * returns 0 if we get a negative match for the hostname or the ip
 * or if we get no match at all.  returns -1 on error, or 1 on
 * successful match.
 */
int
match_host_and_ip(const char *host, const char *ipaddr,
    const char *patterns)
{
	int mhost, mip;

	/* error in ipaddr match */
	if ((mip = addr_match_list(ipaddr, patterns)) == -2)
		return -1;
	else if (mip == -1) /* negative ip address match */
		return 0;

	/* negative hostname match */
	if ((mhost = match_hostname(host, patterns, strlen(patterns))) == -1)
		return 0;
	/* no match at all */
	if (mhost == 0 && mip == 0)
		return 0;
	return 1;
}
示例#7
0
文件: auth.c 项目: ozaki-r/netbsd-src
/*
 * Check if the user is allowed to log in via ssh. If user is listed
 * in DenyUsers or one of user's groups is listed in DenyGroups, false
 * will be returned. If AllowUsers isn't empty and user isn't listed
 * there, or if AllowGroups isn't empty and one of user's groups isn't
 * listed there, false will be returned.
 * If the user's shell is not executable, false will be returned.
 * Otherwise true is returned.
 */
int
allowed_user(struct passwd * pw)
{
#ifdef HAVE_LOGIN_CAP
	extern login_cap_t *lc;
	int match_name, match_ip;
	char *cap_hlist, *hp;
#endif
	struct ssh *ssh = active_state; /* XXX */
	struct stat st;
	const char *hostname = NULL, *ipaddr = NULL;
	int r;
	u_int i;

	/* Shouldn't be called if pw is NULL, but better safe than sorry... */
	if (!pw || !pw->pw_name)
		return 0;

#ifdef HAVE_LOGIN_CAP
	hostname = auth_get_canonical_hostname(ssh, options.use_dns);
	ipaddr = ssh_remote_ipaddr(ssh);

	lc = login_getclass(pw->pw_class);

	/*
	 * Check the deny list.
	 */
	cap_hlist = login_getcapstr(lc, "host.deny", NULL, NULL);
	if (cap_hlist != NULL) {
		hp = strtok(cap_hlist, ",");
		while (hp != NULL) {
			match_name = match_hostname(hostname, hp);
			match_ip = match_hostname(ipaddr, hp);
			/*
			 * Only a positive match here causes a "deny".
			 */
			if (match_name > 0 || match_ip > 0) {
				free(cap_hlist);
				login_close(lc);
				return 0;
			}
			hp = strtok(NULL, ",");
		}
		free(cap_hlist);
	}

	/*
	 * Check the allow list.  If the allow list exists, and the
	 * remote host is not in it, the user is implicitly denied.
	 */
	cap_hlist = login_getcapstr(lc, "host.allow", NULL, NULL);
	if (cap_hlist != NULL) {
		hp = strtok(cap_hlist, ",");
		if (hp == NULL) {
			/* Just in case there's an empty string... */
			free(cap_hlist);
			login_close(lc);
			return 0;
		}
		while (hp != NULL) {
			match_name = match_hostname(hostname, hp);
			match_ip = match_hostname(ipaddr, hp);
			/*
			 * Negative match causes an immediate "deny".
			 * Positive match causes us to break out
			 * of the loop (allowing a fallthrough).
			 */
			if (match_name < 0 || match_ip < 0) {
				free(cap_hlist);
				login_close(lc);
				return 0;
			}
			if (match_name > 0 || match_ip > 0)
				break;
			hp = strtok(NULL, ",");
		}
		free(cap_hlist);
		if (hp == NULL) {
			login_close(lc);
			return 0;
		}
	}

	login_close(lc);
#endif

#ifdef USE_PAM
	if (!options.use_pam) {
#endif
	/*
	 * password/account expiration.
	 */
	if (pw->pw_change || pw->pw_expire) {
		struct timeval tv;

		(void)gettimeofday(&tv, (struct timezone *)NULL);
		if (pw->pw_expire) {
			if (tv.tv_sec >= pw->pw_expire) {
				logit("User %.100s not allowed because account has expired",
				    pw->pw_name);
				return 0;	/* expired */
			}
		}
#ifdef _PASSWORD_CHGNOW
		if (pw->pw_change == _PASSWORD_CHGNOW) {
			logit("User %.100s not allowed because password needs to be changed",
			    pw->pw_name);

			return 0;	/* can't force password change (yet) */
		}
#endif
		if (pw->pw_change) {
			if (tv.tv_sec >= pw->pw_change) {
				logit("User %.100s not allowed because password has expired",
				    pw->pw_name);
				return 0;	/* expired */
			}
		}
	}
#ifdef USE_PAM
	}
#endif

	/*
	 * Deny if shell does not exist or is not executable unless we
	 * are chrooting.
	 */
	/*
	 * XXX Should check to see if it is executable by the
	 * XXX requesting user.  --thorpej
	 */
	if (options.chroot_directory == NULL ||
	    strcasecmp(options.chroot_directory, "none") == 0) {
		char *shell = xstrdup((pw->pw_shell[0] == '\0') ?
		    _PATH_BSHELL : pw->pw_shell); /* empty = /bin/sh */

		if (stat(shell, &st) != 0) {
			logit("User %.100s not allowed because shell %.100s "
			    "does not exist", pw->pw_name, shell);
			free(shell);
			return 0;
		}
		if (S_ISREG(st.st_mode) == 0 ||
		    (st.st_mode & (S_IXOTH|S_IXUSR|S_IXGRP)) == 0) {
			logit("User %.100s not allowed because shell %.100s "
			    "is not executable", pw->pw_name, shell);
			free(shell);
			return 0;
		}
		free(shell);
	}
	/*
	 * XXX Consider nuking {Allow,Deny}{Users,Groups}.  We have the
	 * XXX login_cap(3) mechanism which covers all other types of
	 * XXX logins, too.
	 */

	if (options.num_deny_users > 0 || options.num_allow_users > 0 ||
	    options.num_deny_groups > 0 || options.num_allow_groups > 0) {
		hostname = auth_get_canonical_hostname(ssh, options.use_dns);
		ipaddr = ssh_remote_ipaddr(ssh);
	}

	/* Return false if user is listed in DenyUsers */
	if (options.num_deny_users > 0) {
		for (i = 0; i < options.num_deny_users; i++) {
			r = match_user(pw->pw_name, hostname, ipaddr,
			    options.deny_users[i]);
			if (r < 0) {
				fatal("Invalid DenyUsers pattern \"%.100s\"",
				    options.deny_users[i]);
			} else if (r != 0) {
				logit("User %.100s from %.100s not allowed "
				    "because listed in DenyUsers",
				    pw->pw_name, hostname);
				return 0;
			}
		}
	}
	/* Return false if AllowUsers isn't empty and user isn't listed there */
	if (options.num_allow_users > 0) {
		for (i = 0; i < options.num_allow_users; i++) {
			r = match_user(pw->pw_name, hostname, ipaddr,
			    options.allow_users[i]);
			if (r < 0) {
				fatal("Invalid AllowUsers pattern \"%.100s\"",
				    options.allow_users[i]);
			} else if (r == 1)
				break;
		}
		/* i < options.num_allow_users iff we break for loop */
		if (i >= options.num_allow_users) {
			logit("User %.100s from %.100s not allowed because "
			    "not listed in AllowUsers", pw->pw_name, hostname);
			return 0;
		}
	}
	if (options.num_deny_groups > 0 || options.num_allow_groups > 0) {
		/* Get the user's group access list (primary and supplementary) */
		if (ga_init(pw->pw_name, pw->pw_gid) == 0) {
			logit("User %.100s from %.100s not allowed because "
			    "not in any group", pw->pw_name, hostname);
			return 0;
		}

		/* Return false if one of user's groups is listed in DenyGroups */
		if (options.num_deny_groups > 0)
			if (ga_match(options.deny_groups,
			    options.num_deny_groups)) {
				ga_free();
				logit("User %.100s from %.100s not allowed "
				    "because a group is listed in DenyGroups",
				    pw->pw_name, hostname);
				return 0;
			}
		/*
		 * Return false if AllowGroups isn't empty and one of user's groups
		 * isn't listed there
		 */
		if (options.num_allow_groups > 0)
			if (!ga_match(options.allow_groups,
			    options.num_allow_groups)) {
				ga_free();
				logit("User %.100s from %.100s not allowed "
				    "because none of user's groups are listed "
				    "in AllowGroups", pw->pw_name, hostname);
				return 0;
			}
		ga_free();
	}
	/* We found no reason not to let this user try to log on... */
	return 1;
}
示例#8
0
void
load_hostkeys(struct hostkeys *hostkeys, const char *host, const char *path)
{
	FILE *f;
	char line[8192];
	u_long linenum = 0, num_loaded = 0;
	char *cp, *cp2, *hashed_host;
	HostkeyMarker marker;
	Key *key;
	int kbits;

	if ((f = fopen(path, "r")) == NULL)
		return;
	debug3("%s: loading entries for host \"%.100s\" from file \"%s\"",
	    __func__, host, path);
	while (read_keyfile_line(f, path, line, sizeof(line), &linenum) == 0) {
		cp = line;

		/* Skip any leading whitespace, comments and empty lines. */
		for (; *cp == ' ' || *cp == '\t'; cp++)
			;
		if (!*cp || *cp == '#' || *cp == '\n')
			continue;

		if ((marker = check_markers(&cp)) == MRK_ERROR) {
			verbose("%s: invalid marker at %s:%lu",
			    __func__, path, linenum);
			continue;
		}

		/* Find the end of the host name portion. */
		for (cp2 = cp; *cp2 && *cp2 != ' ' && *cp2 != '\t'; cp2++)
			;

		/* Check if the host name matches. */
		if (match_hostname(host, cp, (u_int) (cp2 - cp)) != 1) {
			if (*cp != HASH_DELIM)
				continue;
			hashed_host = host_hash(host, cp, (u_int) (cp2 - cp));
			if (hashed_host == NULL) {
				debug("Invalid hashed host line %lu of %s",
				    linenum, path);
				continue;
			}
			if (strncmp(hashed_host, cp, (u_int) (cp2 - cp)) != 0)
				continue;
		}

		/* Got a match.  Skip host name. */
		cp = cp2;

		/*
		 * Extract the key from the line.  This will skip any leading
		 * whitespace.  Ignore badly formatted lines.
		 */
		key = key_new(KEY_UNSPEC);
		if (!hostfile_read_key(&cp, &kbits, key)) {
			key_free(key);
			key = key_new(KEY_RSA1);
			if (!hostfile_read_key(&cp, &kbits, key)) {
				key_free(key);
				continue;
			}
		}
		if (!hostfile_check_key(kbits, key, host, path, linenum))
			continue;

		debug3("%s: found %skey type %s in file %s:%lu", __func__,
		    marker == MRK_NONE ? "" :
		    (marker == MRK_CA ? "ca " : "revoked "),
		    key_type(key), path, linenum);
		hostkeys->entries = xrealloc(hostkeys->entries,
		    hostkeys->num_entries + 1, sizeof(*hostkeys->entries));
		hostkeys->entries[hostkeys->num_entries].host = xstrdup(host);
		hostkeys->entries[hostkeys->num_entries].file = xstrdup(path);
		hostkeys->entries[hostkeys->num_entries].line = linenum;
		hostkeys->entries[hostkeys->num_entries].key = key;
		hostkeys->entries[hostkeys->num_entries].marker = marker;
		hostkeys->num_entries++;
		num_loaded++;
	}
	debug3("%s: loaded %lu keys", __func__, num_loaded);
	fclose(f);
	return;
}	
示例#9
0
static int
match_cfg_line(char **condition, int line, const char *user, const char *host,
    const char *address)
{
	int result = 1;
	char *arg, *attrib, *cp = *condition;
	size_t len;

	if (user == NULL)
		debug3("checking syntax for 'Match %s'", cp);
	else
		debug3("checking match for '%s' user %s host %s addr %s", cp,
		    user ? user : "******", host ? host : "(null)",
		    address ? address : "(null)");

	while ((attrib = strdelim(&cp)) && *attrib != '\0') {
		if ((arg = strdelim(&cp)) == NULL || *arg == '\0') {
			error("Missing Match criteria for %s", attrib);
			return -1;
		}
		len = strlen(arg);
		if (strcasecmp(attrib, "user") == 0) {
			if (!user) {
				result = 0;
				continue;
			}
			if (match_pattern_list(user, arg, len, 0) != 1)
				result = 0;
			else
				debug("user %.100s matched 'User %.100s' at "
				    "line %d", user, arg, line);
		} else if (strcasecmp(attrib, "group") == 0) {
			switch (match_cfg_line_group(arg, line, user)) {
			case -1:
				return -1;
			case 0:
				result = 0;
			}
		} else if (strcasecmp(attrib, "host") == 0) {
			if (!host) {
				result = 0;
				continue;
			}
			if (match_hostname(host, arg, len) != 1)
				result = 0;
			else
				debug("connection from %.100s matched 'Host "
				    "%.100s' at line %d", host, arg, line);
		} else if (strcasecmp(attrib, "address") == 0) {
			switch (addr_match_list(address, arg)) {
			case 1:
				debug("connection from %.100s matched 'Address "
				    "%.100s' at line %d", address, arg, line);
				break;
			case 0:
			case -1:
				result = 0;
				break;
			case -2:
				return -1;
			}
		} else {
			error("Unsupported Match attribute %s", attrib);
			return -1;
		}
	}
	if (user != NULL)
		debug3("match %sfound", result ? "" : "not ");
	*condition = cp;
	return result;
}
示例#10
0
/**
 * @brief Check if the server is known.
 *
 * Checks the user's known host file for a previous connection to the
 * current server.
 *
 * @param[in]  session  The SSH session to use.
 *
 * @returns SSH_SERVER_KNOWN_OK:       The server is known and has not changed.\n
 *          SSH_SERVER_KNOWN_CHANGED:  The server key has changed. Either you
 *                                     are under attack or the administrator
 *                                     changed the key. You HAVE to warn the
 *                                     user about a possible attack.\n
 *          SSH_SERVER_FOUND_OTHER:    The server gave use a key of a type while
 *                                     we had an other type recorded. It is a
 *                                     possible attack.\n
 *          SSH_SERVER_NOT_KNOWN:      The server is unknown. User should
 *                                     confirm the MD5 is correct.\n
 *          SSH_SERVER_FILE_NOT_FOUND: The known host file does not exist. The
 *                                     host is thus unknown. File will be
 *                                     created if host key is accepted.\n
 *          SSH_SERVER_ERROR:          Some error happened.
 *
 * @see ssh_get_pubkey_hash()
 *
 * @bug There is no current way to remove or modify an entry into the known
 *      host table.
 */
int ssh_is_server_known(ssh_session session) {
  FILE *file = NULL;
  char **tokens;
  char *host;
  char *hostport;
  const char *type;
  int match;
  int ret = SSH_SERVER_NOT_KNOWN;

  enter_function();

  if (session->knownhosts == NULL) {
    if (ssh_options_apply(session) < 0) {
      ssh_set_error(session, SSH_REQUEST_DENIED,
          "Can't find a known_hosts file");
      leave_function();
      return SSH_SERVER_FILE_NOT_FOUND;
    }
  }

  if (session->host == NULL) {
    ssh_set_error(session, SSH_FATAL,
        "Can't verify host in known hosts if the hostname isn't known");
    leave_function();
    return SSH_SERVER_ERROR;
  }

  if (session->current_crypto == NULL){
  	ssh_set_error(session, SSH_FATAL,
  			"ssh_is_host_known called without cryptographic context");
  	leave_function();
  	return SSH_SERVER_ERROR;
  }
  host = ssh_lowercase(session->host);
  hostport = ssh_hostport(host,session->port);
  if (host == NULL || hostport == NULL) {
    ssh_set_error_oom(session);
    SAFE_FREE(host);
    SAFE_FREE(hostport);
    leave_function();
    return SSH_SERVER_ERROR;
  }

  do {
    tokens = ssh_get_knownhost_line(session, &file,
        session->knownhosts, &type);

    /* End of file, return the current state */
    if (tokens == NULL) {
      break;
    }
    match = match_hashed_host(session, host, tokens[0]);
    if (match == 0){
    	match = match_hostname(hostport, tokens[0], strlen(tokens[0]));
    }
    if (match == 0) {
      match = match_hostname(host, tokens[0], strlen(tokens[0]));
    }
    if (match == 0) {
      match = match_hashed_host(session, hostport, tokens[0]);
    }
    if (match) {
      /* We got a match. Now check the key type */
      if (strcmp(session->current_crypto->server_pubkey_type, type) != 0) {
        /* Different type. We don't override the known_changed error which is
         * more important */
        if (ret != SSH_SERVER_KNOWN_CHANGED)
          ret = SSH_SERVER_FOUND_OTHER;
        tokens_free(tokens);
        continue;
      }
      /* so we know the key type is good. We may get a good key or a bad key. */
      match = check_public_key(session, tokens);
      tokens_free(tokens);

      if (match < 0) {
        ret = SSH_SERVER_ERROR;
        break;
      } else if (match == 1) {
        ret = SSH_SERVER_KNOWN_OK;
        break;
      } else if(match == 0) {
        /* We override the status with the wrong key state */
        ret = SSH_SERVER_KNOWN_CHANGED;
      }
    } else {
      tokens_free(tokens);
    }
  } while (1);

  if ( (ret == SSH_SERVER_NOT_KNOWN) && (session->StrictHostKeyChecking == 0) ) {
    ssh_write_knownhost(session);
    ret = SSH_SERVER_KNOWN_OK;
  }

  SAFE_FREE(host);
  SAFE_FREE(hostport);
  if (file != NULL) {
    fclose(file);
  }

  /* Return the current state at end of file */
  leave_function();
  return ret;
}
示例#11
0
static HostStatus
check_host_in_hostfile_by_key_or_type(const char *filename,
    const char *host, const Key *key, int keytype, Key *found, int *numret)
{
	FILE *f;
	char line[8192];
	int linenum = 0;
	u_int kbits;
	char *cp, *cp2, *hashed_host;
	HostStatus end_return;

	debug3("check_host_in_hostfile: filename %s", filename);

	/* Open the file containing the list of known hosts. */
	f = fopen(filename, "r");
	if (!f)
		return HOST_NEW;

	/*
	 * Return value when the loop terminates.  This is set to
	 * HOST_CHANGED if we have seen a different key for the host and have
	 * not found the proper one.
	 */
	end_return = HOST_NEW;

	/* Go through the file. */
	while (fgets(line, sizeof(line), f)) {
		cp = line;
		linenum++;

		/* Skip any leading whitespace, comments and empty lines. */
		for (; *cp == ' ' || *cp == '\t'; cp++)
			;
		if (!*cp || *cp == '#' || *cp == '\n')
			continue;

		/* Find the end of the host name portion. */
		for (cp2 = cp; *cp2 && *cp2 != ' ' && *cp2 != '\t'; cp2++)
			;

		/* Check if the host name matches. */
		if (match_hostname(host, cp, (u_int) (cp2 - cp)) != 1) {
			if (*cp != HASH_DELIM)
				continue;
			hashed_host = host_hash(host, cp, (u_int) (cp2 - cp));
			if (hashed_host == NULL) {
				debug("Invalid hashed host line %d of %s",
				    linenum, filename);
				continue;
			}
			if (strncmp(hashed_host, cp, (u_int) (cp2 - cp)) != 0)
				continue;
		}

		/* Got a match.  Skip host name. */
		cp = cp2;

		/*
		 * Extract the key from the line.  This will skip any leading
		 * whitespace.  Ignore badly formatted lines.
		 */
		if (!hostfile_read_key(&cp, &kbits, found))
			continue;

		if (numret != NULL)
			*numret = linenum;

		if (key == NULL) {
			/* we found a key of the requested type */
			if (found->type == keytype)
				return HOST_FOUND;
			continue;
		}

		if (!hostfile_check_key(kbits, found, host, filename, linenum))
			continue;

		/* Check if the current key is the same as the given key. */
		if (key_equal(key, found)) {
			/* Ok, they match. */
			debug3("check_host_in_hostfile: match line %d", linenum);
			fclose(f);
			return HOST_OK;
		}
		/*
		 * They do not match.  We will continue to go through the
		 * file; however, we note that we will not return that it is
		 * new.
		 */
		end_return = HOST_CHANGED;
	}
	/* Clear variables and close the file. */
	fclose(f);

	/*
	 * Return either HOST_NEW or HOST_CHANGED, depending on whether we
	 * saw a different key for the host.
	 */
	return end_return;
}
示例#12
0
static int ssh_config_parse_line(ssh_session session, const char *line,
    unsigned int count, int *parsing) {
  enum ssh_config_opcode_e opcode;
  const char *p;
  char *s, *x;
  char *keyword;
  char *lowerhost;
  size_t len;
  int i;

  x = s = strdup(line);
  if (s == NULL) {
    ssh_set_error_oom(session);
    return -1;
  }

  /* Remove trailing spaces */
  for (len = strlen(s) - 1; len > 0; len--) {
    if (! isspace(s[len])) {
      break;
    }
    s[len] = '\0';
  }

  keyword = ssh_config_get_token(&s);
  if (keyword == NULL || *keyword == '#' ||
      *keyword == '\0' || *keyword == '\n') {
    SAFE_FREE(x);
    return 0;
  }

  opcode = ssh_config_get_opcode(keyword);

  switch (opcode) {
    case SOC_HOST:
      *parsing = 0;
      lowerhost = (session->host) ? ssh_lowercase(session->host) : NULL;
      for (p = ssh_config_get_str(&s, NULL); p && *p;
          p = ssh_config_get_str(&s, NULL)) {
        if (match_hostname(lowerhost, p, strlen(p))) {
          *parsing = 1;
        }
      }
      SAFE_FREE(lowerhost);
      break;
    case SOC_HOSTNAME:
      p = ssh_config_get_str(&s, NULL);
      if (p && *parsing) {
        ssh_options_set(session, SSH_OPTIONS_HOST, p);
      }
      break;
    case SOC_PORT:
      if (session->port == 22) {
          p = ssh_config_get_str(&s, NULL);
          if (p && *parsing) {
              ssh_options_set(session, SSH_OPTIONS_PORT_STR, p);
          }
      }
      break;
    case SOC_USERNAME:
      if (session->username == NULL) {
          p = ssh_config_get_str(&s, NULL);
          if (p && *parsing) {
            ssh_options_set(session, SSH_OPTIONS_USER, p);
         }
      }
      break;
    case SOC_IDENTITY:
      p = ssh_config_get_str(&s, NULL);
      if (p && *parsing) {
        ssh_options_set(session, SSH_OPTIONS_ADD_IDENTITY, p);
      }
      break;
    case SOC_CIPHERS:
      p = ssh_config_get_str(&s, NULL);
      if (p && *parsing) {
        ssh_options_set(session, SSH_OPTIONS_CIPHERS_C_S, p);
        ssh_options_set(session, SSH_OPTIONS_CIPHERS_S_C, p);
      }
      break;
    case SOC_COMPRESSION:
      i = ssh_config_get_yesno(&s, -1);
      if (i >= 0 && *parsing) {
        if (i) {
          ssh_options_set(session, SSH_OPTIONS_COMPRESSION, "yes");
        } else {
          ssh_options_set(session, SSH_OPTIONS_COMPRESSION, "no");
        }
      }
      break;
    case SOC_PROTOCOL:
      p = ssh_config_get_str(&s, NULL);
      if (p && *parsing) {
        char *a, *b;
        b = strdup(p);
        if (b == NULL) {
          SAFE_FREE(x);
          ssh_set_error_oom(session);
          return -1;
        }
        i = 0;
        ssh_options_set(session, SSH_OPTIONS_SSH1, &i);
        ssh_options_set(session, SSH_OPTIONS_SSH2, &i);

        for (a = strtok(b, ","); a; a = strtok(NULL, ",")) {
          switch (atoi(a)) {
            case 1:
              i = 1;
              ssh_options_set(session, SSH_OPTIONS_SSH1, &i);
              break;
            case 2:
              i = 1;
              ssh_options_set(session, SSH_OPTIONS_SSH2, &i);
              break;
            default:
              break;
          }
        }
        SAFE_FREE(b);
      }
      break;
    case SOC_TIMEOUT:
      i = ssh_config_get_int(&s, -1);
      if (i >= 0 && *parsing) {
        ssh_options_set(session, SSH_OPTIONS_TIMEOUT, &i);
      }
      break;
    case SOC_STRICTHOSTKEYCHECK:
      i = ssh_config_get_yesno(&s, -1);
      if (i >= 0 && *parsing) {
        ssh_options_set(session, SSH_OPTIONS_STRICTHOSTKEYCHECK, &i);
      }
      break;
    case SOC_KNOWNHOSTS:
      p = ssh_config_get_str(&s, NULL);
      if (p && *parsing) {
        ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, p);
      }
      break;
    case SOC_PROXYCOMMAND:
      p = ssh_config_get_str(&s, NULL);
      if (p && *parsing) {
        ssh_options_set(session, SSH_OPTIONS_PROXYCOMMAND, p);
      }
      break;
    case SOC_UNSUPPORTED:
      ssh_log(session, SSH_LOG_RARE, "Unsupported option: %s, line: %d\n",
              keyword, count);
      break;
    default:
      ssh_set_error(session, SSH_FATAL, "ERROR - unimplemented opcode: %d\n",
              opcode);
      SAFE_FREE(x);
      return -1;
      break;
  }

  SAFE_FREE(x);
  return 0;
}
示例#13
0
/* Check certificate identity.  Returns zero if identity matches; 1 if
 * identity does not match, or <0 if the certificate had no identity.
 * If 'identity' is non-NULL, store the malloc-allocated identity in
 * *identity. */
static int check_identity(const char *hostname, X509 *cert, char **identity)
{
    STACK_OF(GENERAL_NAME) *names;
    int match = 0, found = 0;
    
    names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
    if (names) {
	int n;

        /* subjectAltName contains a sequence of GeneralNames */
	for (n = 0; n < sk_GENERAL_NAME_num(names) && !match; n++) {
	    GENERAL_NAME *nm = sk_GENERAL_NAME_value(names, n);
	    
            /* handle dNSName and iPAddress name extensions only. */
	    if (nm->type == GEN_DNS) {
		char *name = dup_ia5string(nm->d.ia5);
                if (identity && !found) *identity = ne_strdup(name);
		match = match_hostname(name, hostname);
		ne_free(name);
		found = 1;
            } else if (nm->type == GEN_IPADD) {
                /* compare IP address with server IP address. */
                ne_inet_addr *ia;
                if (nm->d.ip->length == 4)
                    ia = ne_iaddr_make(ne_iaddr_ipv4, nm->d.ip->data);
                else if (nm->d.ip->length == 16)
                    ia = ne_iaddr_make(ne_iaddr_ipv6, nm->d.ip->data);
                else
                    ia = NULL;
                /* ne_iaddr_make returns NULL if address type is unsupported */
                if (ia != NULL) { /* address type was supported. */
                    char buf[128];

                    match = strcmp(hostname, 
                                   ne_iaddr_print(ia, buf, sizeof buf)) == 0;
                    found = 1;
                    ne_iaddr_free(ia);
                } else {
                    NE_DEBUG(NE_DBG_SSL, "iPAddress name with unsupported "
                             "address type (length %d), skipped.\n",
                             nm->d.ip->length);
                }
            } /* TODO: handle uniformResourceIdentifier too */

	}
        /* free the whole stack. */
        sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free);
    }
    
    /* Check against the commonName if no DNS alt. names were found,
     * as per RFC3280. */
    if (!found) {
	X509_NAME *subj = X509_get_subject_name(cert);
	X509_NAME_ENTRY *entry;
	ne_buffer *cname = ne_buffer_ncreate(30);
	int idx = -1, lastidx;

	/* find the most specific commonName attribute. */
	do {
	    lastidx = idx;
	    idx = X509_NAME_get_index_by_NID(subj, NID_commonName, lastidx);
	} while (idx >= 0);
	
	if (lastidx < 0) {
            /* no commonNmae attributes at all. */
            ne_buffer_destroy(cname);
	    return -1;
        }

	/* extract the string from the entry */
        entry = X509_NAME_get_entry(subj, lastidx);
        if (append_dirstring(cname, X509_NAME_ENTRY_get_data(entry))) {
            ne_buffer_destroy(cname);
            return -1;
        }
        if (identity) *identity = ne_strdup(cname->data);
        match = match_hostname(cname->data, hostname);
        ne_buffer_destroy(cname);
    }

    NE_DEBUG(NE_DBG_SSL, "Identity match for '%s': %s\n", hostname, 
             match ? "good" : "bad");
    return match ? 0 : 1;
}
示例#14
0
/* Check certificate identity.  Returns zero if identity matches; 1 if
 * identity does not match, or <0 if the certificate had no identity.
 * If 'identity' is non-NULL, store the malloc-allocated identity in
 * *identity.  If 'server' is non-NULL, it must be the network address
 * of the server in use, and identity must be NULL. */
static int check_identity(const char *hostname, gnutls_x509_crt cert,
                          char **identity)
{
    char name[255];
    unsigned int critical;
    int ret, seq = 0;
    int match = 0, found = 0;
    size_t len;

    do {
        len = sizeof name;
        ret = gnutls_x509_crt_get_subject_alt_name(cert, seq, name, &len,
                                                   &critical);
        switch (ret) {
        case GNUTLS_SAN_DNSNAME:
            if (identity && !found) *identity = ne_strdup(name);
            match = match_hostname(name, hostname);
            found = 1;
            break;
        case GNUTLS_SAN_IPADDRESS: {
            ne_inet_addr *ia;
            if (len == 4)
                ia = ne_iaddr_make(ne_iaddr_ipv4, (unsigned char *)name);
            else if (len == 16)
                ia = ne_iaddr_make(ne_iaddr_ipv6, (unsigned char *)name);
            else 
                ia = NULL;
            if (ia) {
                char buf[128];
                
                match = strcmp(hostname, 
                               ne_iaddr_print(ia, buf, sizeof buf)) == 0;
                if (identity) *identity = ne_strdup(buf);
                found = 1;
                ne_iaddr_free(ia);
            } else {
                NE_DEBUG(NE_DBG_SSL, "iPAddress name with unsupported "
                         "address type (length %" NE_FMT_SIZE_T "), skipped.\n",
                         len);
            }
        } break;
        default:
            break;
        }
        seq++;
    } while (!match && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);

    /* Check against the commonName if no DNS alt. names were found,
     * as per RFC3280. */
    if (!found) {
        seq = oid_find_highest_index(cert, 1, GNUTLS_OID_X520_COMMON_NAME);

        if (seq >= 0) {
            len = sizeof name;
            name[0] = '\0';
            ret = gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME,
                                                seq, 0, name, &len);
            if (ret == 0) {
                if (identity) *identity = ne_strdup(name);
                match = match_hostname(name, hostname);
            }
        } else {
            return -1;
        }
    }

    NE_DEBUG(NE_DBG_SSL, "Identity match: %s\n", match ? "good" : "bad");
    return match ? 0 : 1;
}
示例#15
0
static void verify_extract_name(TLS_SESS_STATE *TLScontext, X509 *peercert,
				        const TLS_CLIENT_START_PROPS *props)
{
    int     i;
    int     r;
    int     matched = 0;
    int     dnsname_match;
    int     verify_peername = 0;
    int     log_certmatch;
    int     verbose;
    const char *dnsname;
    const GENERAL_NAME *gn;

    STACK_OF(GENERAL_NAME) * gens;

    /*
     * On exit both peer_CN and issuer_CN should be set.
     */
    TLScontext->issuer_CN = tls_issuer_CN(peercert, TLScontext);

    /*
     * Is the certificate trust chain valid and trusted?
     */
    if (SSL_get_verify_result(TLScontext->con) == X509_V_OK)
	TLScontext->peer_status |= TLS_CERT_FLAG_TRUSTED;

    if (TLS_CERT_IS_TRUSTED(TLScontext) && props->tls_level >= TLS_LEV_VERIFY)
	verify_peername = 1;

    /* Force cert processing so we can log the data? */
    log_certmatch = TLScontext->log_mask & TLS_LOG_CERTMATCH;

    /* Log cert details when processing? */
    verbose = log_certmatch || (TLScontext->log_mask & TLS_LOG_VERBOSE);

    if (verify_peername || log_certmatch) {

	/*
	 * Verify the dNSName(s) in the peer certificate against the nexthop
	 * and hostname.
	 * 
	 * If DNS names are present, we use the first matching (or else simply
	 * the first) DNS name as the subject CN. The CommonName in the
	 * issuer DN is obsolete when SubjectAltName is available. This
	 * yields much less surprising logs, because we log the name we
	 * verified or a name we checked and failed to match.
	 * 
	 * XXX: The nexthop and host name may both be the same network address
	 * rather than a DNS name. In this case we really should be looking
	 * for GEN_IPADD entries, not GEN_DNS entries.
	 * 
	 * XXX: In ideal world the caller who used the address to build the
	 * connection would tell us that the nexthop is the connection
	 * address, but if that is not practical, we can parse the nexthop
	 * again here.
	 */
	gens = X509_get_ext_d2i(peercert, NID_subject_alt_name, 0, 0);
	if (gens) {
	    r = sk_GENERAL_NAME_num(gens);
	    for (i = 0; i < r; ++i) {
		gn = sk_GENERAL_NAME_value(gens, i);
		if (gn->type != GEN_DNS)
		    continue;

		/*
		 * Even if we have an invalid DNS name, we still ultimately
		 * ignore the CommonName, because subjectAltName:DNS is
		 * present (though malformed). Replace any previous peer_CN
		 * if empty or we get a match.
		 * 
		 * We always set at least an empty peer_CN if the ALTNAME cert
		 * flag is set. If not, we set peer_CN from the cert
		 * CommonName below, so peer_CN is always non-null on return.
		 */
		TLScontext->peer_status |= TLS_CERT_FLAG_ALTNAME;
		dnsname = tls_dns_name(gn, TLScontext);
		if (dnsname && *dnsname) {
		    if ((dnsname_match = match_hostname(dnsname, props)) != 0)
			matched++;
		    /* Keep the first matched name. */
		    if (TLScontext->peer_CN
			&& ((dnsname_match && matched == 1)
			    || *TLScontext->peer_CN == 0)) {
			myfree(TLScontext->peer_CN);
			TLScontext->peer_CN = 0;
		    }
		    if (verbose)
			msg_info("%s: %ssubjectAltName: %s", props->namaddr,
				 dnsname_match ? "Matched " : "", dnsname);
		}
		if (TLScontext->peer_CN == 0)
		    TLScontext->peer_CN = mystrdup(dnsname ? dnsname : "");
		if (matched && !log_certmatch)
		    break;
	    }
	    if (verify_peername && matched)
		TLScontext->peer_status |= TLS_CERT_FLAG_MATCHED;

	    /*
	     * (Sam Rushing, Ironport) Free stack *and* member GENERAL_NAME
	     * objects
	     */
	    sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free);
	}

	/*
	 * No subjectAltNames, peer_CN is taken from CommonName.
	 */
	if (TLScontext->peer_CN == 0) {
	    TLScontext->peer_CN = tls_peer_CN(peercert, TLScontext);
	    if (*TLScontext->peer_CN)
		matched = match_hostname(TLScontext->peer_CN, props);
	    if (verify_peername && matched)
		TLScontext->peer_status |= TLS_CERT_FLAG_MATCHED;
	    if (verbose)
		msg_info("%s %sCommonName %s", props->namaddr,
			 matched ? "Matched " : "", TLScontext->peer_CN);
	} else if (verbose) {
	    char   *tmpcn = tls_peer_CN(peercert, TLScontext);

	    /*
	     * Though the CommonName was superceded by a subjectAltName, log
	     * it when certificate match debugging was requested.
	     */
	    msg_info("%s CommonName %s", TLScontext->namaddr, tmpcn);
	    myfree(tmpcn);
	}
    } else
	TLScontext->peer_CN = tls_peer_CN(peercert, TLScontext);

    /*
     * Give them a clue. Problems with trust chain verification were logged
     * when the session was first negotiated, before the session was stored
     * into the cache. We don't want mystery failures, so log the fact the
     * real problem is to be found in the past.
     */
    if (TLScontext->session_reused
	&& !TLS_CERT_IS_TRUSTED(TLScontext)
	&& (TLScontext->log_mask & TLS_LOG_UNTRUSTED))
	msg_info("%s: re-using session with untrusted certificate, "
		 "look for details earlier in the log", props->namaddr);
}
示例#16
0
/* cf. RFC2818 and RFC2459 */
static int match_cert_hostname(struct openconnect_info *vpninfo, X509 *peer_cert)
{
	STACK_OF(GENERAL_NAME) *altnames;
	X509_NAME *subjname;
	ASN1_STRING *subjasn1;
	char *subjstr = NULL;
	int addrlen = 0;
	int i, altdns = 0;
	char addrbuf[sizeof(struct in6_addr)];
	int ret;

	/* Allow GEN_IP in the certificate only if we actually connected
	   by IP address rather than by name. */
	if (inet_pton(AF_INET, vpninfo->hostname, addrbuf) > 0)
		addrlen = 4;
	else if (inet_pton(AF_INET6, vpninfo->hostname, addrbuf) > 0)
		addrlen = 16;
	else if (vpninfo->hostname[0] == '[' &&
		 vpninfo->hostname[strlen(vpninfo->hostname)-1] == ']') {
		char *p = &vpninfo->hostname[strlen(vpninfo->hostname)-1];
		*p = 0;
		if (inet_pton(AF_INET6, vpninfo->hostname + 1, addrbuf) > 0)
			addrlen = 16;
		*p = ']';
	}

	altnames = X509_get_ext_d2i(peer_cert, NID_subject_alt_name,
				    NULL, NULL);
	for (i = 0; i < sk_GENERAL_NAME_num(altnames); i++) {
		const GENERAL_NAME *this = sk_GENERAL_NAME_value(altnames, i);

		if (this->type == GEN_DNS) {
			char *str;

			int len = ASN1_STRING_to_UTF8((void *)&str, this->d.ia5);
			if (len < 0)
				continue;

			altdns = 1;

			/* We don't like names with embedded NUL */
			if (strlen(str) != len)
				continue;

			if (!match_hostname(vpninfo->hostname, str)) {
				vpn_progress(vpninfo, PRG_TRACE,
					     _("Matched DNS altname '%s'\n"),
					     str);
				GENERAL_NAMES_free(altnames);
				OPENSSL_free(str);
				return 0;
			} else {
				vpn_progress(vpninfo, PRG_TRACE,
					     _("No match for altname '%s'\n"),
					     str);
			}
			OPENSSL_free(str);
		} else if (this->type == GEN_IPADD && addrlen) {
			char host[80];
			int family;

			if (this->d.ip->length == 4) {
				family = AF_INET;
			} else if (this->d.ip->length == 16) {
				family = AF_INET6;
			} else {
				vpn_progress(vpninfo, PRG_ERR,
					     _("Certificate has GEN_IPADD altname with bogus length %d\n"),
					     this->d.ip->length);
				continue;
			}

			/* We only do this for the debug messages */
			inet_ntop(family, this->d.ip->data, host, sizeof(host));

			if (this->d.ip->length == addrlen &&
			    !memcmp(addrbuf, this->d.ip->data, addrlen)) {
				vpn_progress(vpninfo, PRG_TRACE,
					     _("Matched %s address '%s'\n"),
					     (family == AF_INET6) ? "IPv6" : "IPv4",
					     host);
				GENERAL_NAMES_free(altnames);
				return 0;
			} else {
				vpn_progress(vpninfo, PRG_TRACE,
					     _("No match for %s address '%s'\n"),
					     (family == AF_INET6) ? "IPv6" : "IPv4",
					     host);
			}
		} else if (this->type == GEN_URI) {
			char *str;
			char *url_proto, *url_host, *url_path, *url_host2;
			int url_port;
			int len = ASN1_STRING_to_UTF8((void *)&str, this->d.ia5);

			if (len < 0)
				continue;

			/* We don't like names with embedded NUL */
			if (strlen(str) != len)
				continue;

			if (internal_parse_url(str, &url_proto, &url_host, &url_port, &url_path, 0)) {
				OPENSSL_free(str);
				continue;
			}

			if (!url_proto || strcasecmp(url_proto, "https"))
				goto no_uri_match;

			if (url_port != vpninfo->port)
				goto no_uri_match;

			/* Leave url_host as it was so that it can be freed */
			url_host2 = url_host;
			if (addrlen == 16 && vpninfo->hostname[0] != '[' &&
			    url_host[0] == '[' && url_host[strlen(url_host)-1] == ']') {
				/* Cope with https://[IPv6]/ when the hostname is bare IPv6 */
				url_host[strlen(url_host)-1] = 0;
				url_host2++;
			}

			if (strcasecmp(vpninfo->hostname, url_host2))
				goto no_uri_match;

			if (url_path) {
				vpn_progress(vpninfo, PRG_TRACE,
					     _("URI '%s' has non-empty path; ignoring\n"),
					     str);
				goto no_uri_match_silent;
			}
			vpn_progress(vpninfo, PRG_TRACE,
				     _("Matched URI '%s'\n"),
				     str);
			free(url_proto);
			free(url_host);
			free(url_path);
			OPENSSL_free(str);
			GENERAL_NAMES_free(altnames);
			return 0;

		no_uri_match:
			vpn_progress(vpninfo, PRG_TRACE,
				     _("No match for URI '%s'\n"),
				     str);
		no_uri_match_silent:
			free(url_proto);
			free(url_host);
			free(url_path);
			OPENSSL_free(str);
		}
	}
	GENERAL_NAMES_free(altnames);

	/* According to RFC2818, we don't use the legacy subject name if
	   there was an altname with DNS type. */
	if (altdns) {
		vpn_progress(vpninfo, PRG_ERR,
			     _("No altname in peer cert matched '%s'\n"),
			     vpninfo->hostname);
		return -EINVAL;
	}

	subjname = X509_get_subject_name(peer_cert);
	if (!subjname) {
		vpn_progress(vpninfo, PRG_ERR,
			     _("No subject name in peer cert!\n"));
		return -EINVAL;
	}

	/* Find the _last_ (most specific) commonName */
	i = -1;
	while (1) {
		int j = X509_NAME_get_index_by_NID(subjname, NID_commonName, i);
		if (j >= 0)
			i = j;
		else
			break;
	}

	subjasn1 = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(subjname, i));

	i = ASN1_STRING_to_UTF8((void *)&subjstr, subjasn1);

	if (!subjstr || strlen(subjstr) != i) {
		vpn_progress(vpninfo, PRG_ERR,
			     _("Failed to parse subject name in peer cert\n"));
		return -EINVAL;
	}
	ret = 0;

	if (match_hostname(vpninfo->hostname, subjstr)) {
		vpn_progress(vpninfo, PRG_ERR,
			     _("Peer cert subject mismatch ('%s' != '%s')\n"),
			     subjstr, vpninfo->hostname);
		ret = -EINVAL;
	} else {
		vpn_progress(vpninfo, PRG_TRACE,
			     _("Matched peer certificate subject name '%s'\n"),
			     subjstr);
	}

	OPENSSL_free(subjstr);
	return ret;
}
示例#17
0
/* static int check_identity(const nussl_uri *server, X509 *cert, char **identity) */
static int check_identity(const char *expected_hostname, X509 * cert, char **identity)
{
	STACK_OF(GENERAL_NAME) * names;
	int match = 0, found = 0;
	char *found_hostname = NULL;

/*     hostname = server ? server->host : ""; */

	names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
	/* if expected_hostname is NULL, do not check subjectAltName,
	 * we are only looking for the CN
	 */
	if (names && expected_hostname != NULL) {
		int n;

		/* subjectAltName contains a sequence of GeneralNames */
		for (n = 0; n < sk_GENERAL_NAME_num(names) && !match; n++) {
			GENERAL_NAME *nm = sk_GENERAL_NAME_value(names, n);

			/* handle dNSName and iPAddress name extensions only. */
			if (nm->type == GEN_DNS) {
				found_hostname = dup_ia5string(nm->d.ia5);
				match = match_hostname(found_hostname, expected_hostname);
				if (match) {
					found = 1;
					if (identity)
						*identity = nussl_strdup(found_hostname);
				}
			}
			else
			if (nm->type == GEN_IPADD) {
				/* compare IP address with server IP address. */
				nussl_inet_addr *ia;
				if (nm->d.ip->length == 4)
					ia = nussl_iaddr_make(nussl_iaddr_ipv4, nm->d.ip->data);
				else if (nm->d.ip->length == 16)
					ia = nussl_iaddr_make(nussl_iaddr_ipv6, nm->d.ip->data);
				else
					ia = NULL;
				/* nussl_iaddr_make returns NULL if address type is unsupported */
/*                 if (ia != NULL) { /\* address type was supported. *\/ */
/*                     char buf[128]; */

/*                     match = strcmp(hostname,  */
/*                                    nussl_iaddr_print(ia, buf, sizeof buf)) == 0; */
/*                     found = 1; */
/*                     nussl_iaddr_free(ia); */
/*                 } else { */
/*                     NUSSL_DEBUG(NUSSL_DBG_SSL, "iPAddress name with unsupported " */
/*                              "address type (length %d), skipped.\n", */
/*                              nm->d.ip->length); */
/*                 } */
			}
/*             else if (nm->type == GEN_URI) { */
/*                 char *name = dup_ia5string(nm->d.ia5); */
/*                 nussl_uri uri; */

/*                 if (nussl_uri_parse(name, &uri) == 0 && uri.host && uri.scheme) { */
/*                     nussl_uri tmp; */

/*                     if (identity && !found) *identity = nussl_strdup(name); */
/*                     found = 1; */

/*                     if (server) { */
/*                         /\* For comparison purposes, all that matters is */
/*                          * host, scheme and port; ignore the rest. *\/ */
/*                         memset(&tmp, 0, sizeof tmp); */
/*                         tmp.host = uri.host; */
/*                         tmp.scheme = uri.scheme; */
/*                         tmp.port = uri.port; */

/*                         match = nussl_uri_cmp(server, &tmp) == 0; */
/*                     } */
/*                 } */

/*                 nussl_uri_free(&uri); */
/*                 nussl_free(name); */
/*             } */
		}
	}

	/* free the whole stack. */
	if (names)
		sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free);

	/* Check against the commonName if no DNS alt. names were found,
	 * as per RFC3280. */
	if (!found) {
		X509_NAME *subj = X509_get_subject_name(cert);
		X509_NAME_ENTRY *entry;
		nussl_buffer *cname = nussl_buffer_ncreate(30);
		int idx = -1, lastidx;

		/* find the most specific commonName attribute. */
		do {
			lastidx = idx;
			idx = X509_NAME_get_index_by_NID(subj,
						       NID_commonName,
						       lastidx);
		} while (idx >= 0);

		/* check commonName attribute if present */
		if (lastidx >= 0) {
			/* extract the string from the entry */
			entry = X509_NAME_get_entry(subj, lastidx);
			if (append_dirstring(cname, X509_NAME_ENTRY_get_data(entry))) {
				nussl_buffer_destroy(cname);
				return -1;
			}

			found_hostname = nussl_strdup(cname->data);
			if (expected_hostname != NULL)
				match = match_hostname(found_hostname, expected_hostname);
		}
		nussl_buffer_destroy(cname);
	}

	if (found_hostname == NULL)
		return 1;

	/*NUSSL_DEBUG(NUSSL_DBG_SSL, "Identity match for '%s' (identity: %s): %s\n",
			expected_hostname, found_hostname,
			match ? "good" : "bad");*/
	if (identity != NULL)
		*identity = nussl_strdup(found_hostname);
	nussl_free(found_hostname);
	return match ? 0 : 1;
}