int hostkeys_foreach(const char *path, hostkeys_foreach_fn *callback, void *ctx, const char *host, const char *ip, u_int options) { FILE *f; char line[8192], oline[8192], ktype[128]; u_long linenum = 0; char *cp, *cp2; u_int kbits; int hashed; int s, r = 0; struct hostkey_foreach_line lineinfo; size_t l; memset(&lineinfo, 0, sizeof(lineinfo)); if (host == NULL && (options & HKF_WANT_MATCH) != 0) return SSH_ERR_INVALID_ARGUMENT; if ((f = fopen(path, "r")) == NULL) return SSH_ERR_SYSTEM_ERROR; debug3("%s: reading file \"%s\"", __func__, path); while (read_keyfile_line(f, path, line, sizeof(line), &linenum) == 0) { line[strcspn(line, "\n")] = '\0'; strlcpy(oline, line, sizeof(oline)); sshkey_free(lineinfo.key); memset(&lineinfo, 0, sizeof(lineinfo)); lineinfo.path = path; lineinfo.linenum = linenum; lineinfo.line = oline; lineinfo.marker = MRK_NONE; lineinfo.status = HKF_STATUS_OK; lineinfo.keytype = KEY_UNSPEC; /* Skip any leading whitespace, comments and empty lines. */ for (cp = line; *cp == ' ' || *cp == '\t'; cp++) ; if (!*cp || *cp == '#' || *cp == '\n') { if ((options & HKF_WANT_MATCH) == 0) { lineinfo.status = HKF_STATUS_COMMENT; if ((r = callback(&lineinfo, ctx)) != 0) break; } continue; } if ((lineinfo.marker = check_markers(&cp)) == MRK_ERROR) { verbose("%s: invalid marker at %s:%lu", __func__, path, linenum); if ((options & HKF_WANT_MATCH) == 0) goto bad; continue; } /* Find the end of the host name portion. */ for (cp2 = cp; *cp2 && *cp2 != ' ' && *cp2 != '\t'; cp2++) ; lineinfo.hosts = cp; *cp2++ = '\0'; /* Check if the host name matches. */ if (host != NULL) { if ((s = match_maybe_hashed(host, lineinfo.hosts, &hashed)) == -1) { debug2("%s: %s:%ld: bad host hash \"%.32s\"", __func__, path, linenum, lineinfo.hosts); goto bad; } if (s == 1) { lineinfo.status = HKF_STATUS_MATCHED; lineinfo.match |= HKF_MATCH_HOST | (hashed ? HKF_MATCH_HOST_HASHED : 0); } /* Try matching IP address if supplied */ if (ip != NULL) { if ((s = match_maybe_hashed(ip, lineinfo.hosts, &hashed)) == -1) { debug2("%s: %s:%ld: bad ip hash " "\"%.32s\"", __func__, path, linenum, lineinfo.hosts); goto bad; } if (s == 1) { lineinfo.status = HKF_STATUS_MATCHED; lineinfo.match |= HKF_MATCH_IP | (hashed ? HKF_MATCH_IP_HASHED : 0); } } /* * Skip this line if host matching requested and * neither host nor address matched. */ if ((options & HKF_WANT_MATCH) != 0 && lineinfo.status != HKF_STATUS_MATCHED) continue; } /* Got a match. Skip host name and any following whitespace */ for (; *cp2 == ' ' || *cp2 == '\t'; cp2++) ; if (*cp2 == '\0' || *cp2 == '#') { debug2("%s:%ld: truncated before key type", path, linenum); goto bad; } lineinfo.rawkey = cp = cp2; if ((options & HKF_WANT_PARSE_KEY) != 0) { /* * Extract the key from the line. This will skip * any leading whitespace. Ignore badly formatted * lines. */ if ((lineinfo.key = sshkey_new(KEY_UNSPEC)) == NULL) { error("%s: sshkey_new failed", __func__); r = SSH_ERR_ALLOC_FAIL; break; } if (!hostfile_read_key(&cp, &kbits, lineinfo.key)) { #ifdef WITH_SSH1 sshkey_free(lineinfo.key); lineinfo.key = sshkey_new(KEY_RSA1); if (lineinfo.key == NULL) { error("%s: sshkey_new fail", __func__); r = SSH_ERR_ALLOC_FAIL; break; } if (!hostfile_read_key(&cp, &kbits, lineinfo.key)) goto bad; #else goto bad; #endif } lineinfo.keytype = lineinfo.key->type; lineinfo.comment = cp; } else { /* Extract and parse key type */ l = strcspn(lineinfo.rawkey, " \t"); if (l <= 1 || l >= sizeof(ktype) || lineinfo.rawkey[l] == '\0') goto bad; memcpy(ktype, lineinfo.rawkey, l); ktype[l] = '\0'; lineinfo.keytype = sshkey_type_from_name(ktype); /* * Assume RSA1 if the first component is a short * decimal number. */ if (lineinfo.keytype == KEY_UNSPEC && l < 8 && strspn(ktype, "0123456789") == l) lineinfo.keytype = KEY_RSA1; /* * Check that something other than whitespace follows * the key type. This won't catch all corruption, but * it does catch trivial truncation. */ cp2 += l; /* Skip past key type */ for (; *cp2 == ' ' || *cp2 == '\t'; cp2++) ; if (*cp2 == '\0' || *cp2 == '#') { debug2("%s:%ld: truncated after key type", path, linenum); lineinfo.keytype = KEY_UNSPEC; } if (lineinfo.keytype == KEY_UNSPEC) { bad: sshkey_free(lineinfo.key); lineinfo.key = NULL; lineinfo.status = HKF_STATUS_INVALID; if ((r = callback(&lineinfo, ctx)) != 0) break; continue; } } if ((r = callback(&lineinfo, ctx)) != 0) break; } sshkey_free(lineinfo.key); fclose(f); return r; }
int hostkeys_foreach(const char *path, hostkeys_foreach_fn *callback, void *ctx, const char *host, u_int options) { FILE *f; char line[8192], oline[8192]; u_long linenum = 0; char *cp, *cp2; u_int kbits; int s, r = 0; struct hostkey_foreach_line lineinfo; memset(&lineinfo, 0, sizeof(lineinfo)); if (host == NULL && (options & HKF_WANT_MATCH_HOST) != 0) return SSH_ERR_INVALID_ARGUMENT; if ((f = fopen(path, "r")) == NULL) return SSH_ERR_SYSTEM_ERROR; debug3("%s: reading file \"%s\"", __func__, path); while (read_keyfile_line(f, path, line, sizeof(line), &linenum) == 0) { line[strcspn(line, "\n")] = '\0'; strlcpy(oline, line, sizeof(oline)); sshkey_free(lineinfo.key); memset(&lineinfo, 0, sizeof(lineinfo)); lineinfo.path = path; lineinfo.linenum = linenum; lineinfo.line = oline; lineinfo.status = HKF_STATUS_OK; /* Skip any leading whitespace, comments and empty lines. */ for (cp = line; *cp == ' ' || *cp == '\t'; cp++) ; if (!*cp || *cp == '#' || *cp == '\n') { if ((options & HKF_WANT_MATCH_HOST) == 0) { lineinfo.status = HKF_STATUS_COMMENT; if ((r = callback(&lineinfo, ctx)) != 0) break; } continue; } if ((lineinfo.marker = check_markers(&cp)) == MRK_ERROR) { verbose("%s: invalid marker at %s:%lu", __func__, path, linenum); if ((options & HKF_WANT_MATCH_HOST) == 0) goto bad; continue; } /* Find the end of the host name portion. */ for (cp2 = cp; *cp2 && *cp2 != ' ' && *cp2 != '\t'; cp2++) ; lineinfo.hosts = cp; *cp2++ = '\0'; /* Check if the host name matches. */ if (host != NULL) { s = match_maybe_hashed(host, lineinfo.hosts, &lineinfo.was_hashed); if (s == 1) lineinfo.status = HKF_STATUS_HOST_MATCHED; else if ((options & HKF_WANT_MATCH_HOST) != 0) continue; else if (s == -1) { debug2("%s: %s:%ld: bad host hash \"%.32s\"", __func__, path, linenum, lineinfo.hosts); goto bad; } } /* Got a match. Skip host name and any following whitespace */ for (; *cp2 == ' ' || *cp2 == '\t'; cp2++) ; if (*cp2 == '\0' || *cp2 == '#') { debug2("%s:%ld: truncated before key", path, linenum); goto bad; } lineinfo.rawkey = cp = cp2; if ((options & HKF_WANT_PARSE_KEY) != 0) { /* * Extract the key from the line. This will skip * any leading whitespace. Ignore badly formatted * lines. */ if ((lineinfo.key = sshkey_new(KEY_UNSPEC)) == NULL) { error("%s: sshkey_new failed", __func__); return SSH_ERR_ALLOC_FAIL; } if (!hostfile_read_key(&cp, &kbits, lineinfo.key)) { #ifdef WITH_SSH1 sshkey_free(lineinfo.key); lineinfo.key = sshkey_new(KEY_RSA1); if (lineinfo.key == NULL) { error("%s: sshkey_new fail", __func__); return SSH_ERR_ALLOC_FAIL; } if (!hostfile_read_key(&cp, &kbits, lineinfo.key)) goto bad; #else goto bad; #endif } if (!hostfile_check_key(kbits, lineinfo.key, host, path, linenum)) { bad: lineinfo.status = HKF_STATUS_INVALID; if ((r = callback(&lineinfo, ctx)) != 0) break; continue; } } if ((r = callback(&lineinfo, ctx)) != 0) break; } sshkey_free(lineinfo.key); fclose(f); return r; }