/* disable nagle on socket */ void pamsshagentauth_set_nodelay(int fd) { int opt; socklen_t optlen; optlen = sizeof opt; if (getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, &optlen) == -1) { pamsshagentauth_verbose("getsockopt TCP_NODELAY: %.100s", strerror(errno)); return; } if (opt == 1) { pamsshagentauth_verbose("fd %d is TCP_NODELAY", fd); return; } opt = 1; pamsshagentauth_verbose("fd %d setting TCP_NODELAY", fd); if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1) pamsshagentauth_logerror("setsockopt TCP_NODELAY: %.100s", strerror(errno)); }
/* * Returns a standardized host+port identifier string. * Caller must free returned string. */ char * pamsshagentauth_put_host_port(const char *host, u_short port) { char *hoststr; if (port == 0 || port == SSH_DEFAULT_PORT) return(pamsshagentauth_xstrdup(host)); if (asprintf(&hoststr, "[%s]:%d", host, (int)port) < 0) pamsshagentauth_fatal("put_host_port: asprintf: %s", strerror(errno)); pamsshagentauth_verbose("put_host_port: %s", hoststr); return hoststr; }
/* set/unset filedescriptor to non-blocking */ int pamsshagentauth_set_nonblock(int fd) { int val; val = fcntl(fd, F_GETFL, 0); if (val < 0) { pamsshagentauth_logerror("fcntl(%d, F_GETFL, 0): %s", fd, strerror(errno)); return (-1); } if (val & O_NONBLOCK) { pamsshagentauth_verbose("fd %d is O_NONBLOCK", fd); return (0); } pamsshagentauth_verbose("fd %d setting O_NONBLOCK", fd); val |= O_NONBLOCK; if (fcntl(fd, F_SETFL, val) == -1) { pamsshagentauth_verbose("fcntl(%d, F_SETFL, O_NONBLOCK): %s", fd, strerror(errno)); return (-1); } return (0); }
/* * Read an entire line from a public key file into a static buffer, discarding * lines that exceed the buffer size. Returns 0 on success, -1 on failure. */ int read_keyfile_line(FILE *f, const char *filename, char *buf, size_t bufsz, u_long *lineno) { while (fgets(buf, bufsz, f) != NULL) { if (buf[0] == '\0') continue; (*lineno)++; if (buf[strlen(buf) - 1] == '\n' || feof(f)) { return 0; } else { pamsshagentauth_verbose("%s: %s line %lu exceeds size limit", __func__, filename, *lineno); /* discard remainder of line */ while (fgetc(f) != '\n' && !feof(f)) ; /* nothing */ } } return -1; }
int pamsshagentauth_auth_secure_path(const char *name, struct stat *stp, const char *pw_dir, uid_t uid, char *err, size_t errlen) { char buf[MAXPATHLEN], homedir[MAXPATHLEN]; char *cp; int comparehome = 0; struct stat st; pamsshagentauth_verbose("auth_secure_filename: checking for uid: %u", uid); if (realpath(name, buf) == NULL) { snprintf(err, errlen, "realpath %s failed: %s", name, strerror(errno)); return -1; } /* if (realpath(pw->pw_dir, homedir) != NULL) */ if (pw_dir != NULL && realpath(pw_dir, homedir) != NULL) comparehome = 1; /* check the open file to avoid races */ /* * if (fstat(fileno(f), &st) < 0 || * (st.st_uid != 0 && st.st_uid != uid) || * (st.st_mode & 022) != 0) { */ if (!S_ISREG(stp->st_mode)) { snprintf(err, errlen, "%s is not a regular file", buf); return -1; } if ((stp->st_uid != 0 && stp->st_uid != uid) || (stp->st_mode & 022) != 0) { snprintf(err, errlen, "bad ownership or modes for file %s", buf); return -1; } /* for each component of the canonical path, walking upwards */ for (;;) { if ((cp = dirname(buf)) == NULL) { snprintf(err, errlen, "dirname() failed"); return -1; } pamsshagentauth_strlcpy(buf, cp, sizeof(buf)); pamsshagentauth_verbose("secure_filename: checking '%s'", buf); if (stat(buf, &st) < 0 || (st.st_uid != 0 && st.st_uid != uid) || (st.st_mode & 022) != 0) { snprintf(err, errlen, "bad ownership or modes for directory %s", buf); return -1; } /* If are passed the homedir then we can stop */ if (comparehome && strcmp(homedir, buf) == 0) { pamsshagentauth_verbose("secure_filename: terminating check at '%s'", buf); break; } /* * dirname should always complete with a "/" path, * but we can be paranoid and check for "." too */ if ((strcmp("/", buf) == 0) || (strcmp(".", buf) == 0)) break; } return 0; }
/* RSASSA-PKCS1-v1_5 (PKCS #1 v2.0 signature) with SHA1 */ int ssh_rsa_sign(const Key *key, u_char **sigp, u_int *lenp, const u_char *data, u_int datalen) { const EVP_MD *evp_md; EVP_MD_CTX md; u_char digest[EVP_MAX_MD_SIZE], *sig; u_int slen, dlen, len; int ok, nid; Buffer b; if (key == NULL || key->type != KEY_RSA || key->rsa == NULL) { pamsshagentauth_logerror("ssh_rsa_sign: no RSA key"); return -1; } nid = (datafellows & SSH_BUG_RSASIGMD5) ? NID_md5 : NID_sha1; if ((evp_md = EVP_get_digestbynid(nid)) == NULL) { pamsshagentauth_logerror("ssh_rsa_sign: EVP_get_digestbynid %d failed", nid); return -1; } EVP_DigestInit(&md, evp_md); EVP_DigestUpdate(&md, data, datalen); EVP_DigestFinal(&md, digest, &dlen); slen = RSA_size(key->rsa); sig = pamsshagentauth_xmalloc(slen); ok = RSA_sign(nid, digest, dlen, sig, &len, key->rsa); memset(digest, 'd', sizeof(digest)); if (ok != 1) { int ecode = ERR_get_error(); pamsshagentauth_logerror("ssh_rsa_sign: RSA_sign failed: %s", ERR_error_string(ecode, NULL)); pamsshagentauth_xfree(sig); return -1; } if (len < slen) { u_int diff = slen - len; pamsshagentauth_verbose("slen %u > len %u", slen, len); memmove(sig + diff, sig, len); memset(sig, 0, diff); } else if (len > slen) { pamsshagentauth_logerror("ssh_rsa_sign: slen %u slen2 %u", slen, len); pamsshagentauth_xfree(sig); return -1; } /* encode signature */ pamsshagentauth_buffer_init(&b); pamsshagentauth_buffer_put_cstring(&b, "ssh-rsa"); pamsshagentauth_buffer_put_string(&b, sig, slen); len = pamsshagentauth_buffer_len(&b); if (lenp != NULL) *lenp = len; if (sigp != NULL) { *sigp = pamsshagentauth_xmalloc(len); memcpy(*sigp, pamsshagentauth_buffer_ptr(&b), len); } pamsshagentauth_buffer_free(&b); memset(sig, 's', slen); pamsshagentauth_xfree(sig); return 0; }
int ssh_rsa_verify(const Key *key, const u_char *signature, u_int signaturelen, const u_char *data, u_int datalen) { Buffer b; const EVP_MD *evp_md; EVP_MD_CTX md; char *ktype; u_char digest[EVP_MAX_MD_SIZE], *sigblob; u_int len, dlen, modlen; int rlen, ret, nid; if (key == NULL || key->type != KEY_RSA || key->rsa == NULL) { pamsshagentauth_logerror("ssh_rsa_verify: no RSA key"); return -1; } if (BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE) { pamsshagentauth_logerror("ssh_rsa_verify: RSA modulus too small: %d < minimum %d bits", BN_num_bits(key->rsa->n), SSH_RSA_MINIMUM_MODULUS_SIZE); return -1; } pamsshagentauth_buffer_init(&b); pamsshagentauth_buffer_append(&b, signature, signaturelen); ktype = pamsshagentauth_buffer_get_string(&b, NULL); if (strcmp("ssh-rsa", ktype) != 0) { pamsshagentauth_logerror("ssh_rsa_verify: cannot handle type %s", ktype); pamsshagentauth_buffer_free(&b); pamsshagentauth_xfree(ktype); return -1; } pamsshagentauth_xfree(ktype); sigblob = pamsshagentauth_buffer_get_string(&b, &len); rlen = pamsshagentauth_buffer_len(&b); pamsshagentauth_buffer_free(&b); if (rlen != 0) { pamsshagentauth_logerror("ssh_rsa_verify: remaining bytes in signature %d", rlen); pamsshagentauth_xfree(sigblob); return -1; } /* RSA_verify expects a signature of RSA_size */ modlen = RSA_size(key->rsa); if (len > modlen) { pamsshagentauth_logerror("ssh_rsa_verify: len %u > modlen %u", len, modlen); pamsshagentauth_xfree(sigblob); return -1; } else if (len < modlen) { u_int diff = modlen - len; pamsshagentauth_verbose("ssh_rsa_verify: add padding: modlen %u > len %u", modlen, len); sigblob = pamsshagentauth_xrealloc(sigblob, 1, modlen); memmove(sigblob + diff, sigblob, len); memset(sigblob, 0, diff); len = modlen; } nid = (datafellows & SSH_BUG_RSASIGMD5) ? NID_md5 : NID_sha1; if ((evp_md = EVP_get_digestbynid(nid)) == NULL) { pamsshagentauth_logerror("ssh_rsa_verify: EVP_get_digestbynid %d failed", nid); pamsshagentauth_xfree(sigblob); return -1; } EVP_DigestInit(&md, evp_md); EVP_DigestUpdate(&md, data, datalen); EVP_DigestFinal(&md, digest, &dlen); ret = openssh_RSA_verify(nid, digest, dlen, sigblob, len, key->rsa); memset(digest, 'd', sizeof(digest)); memset(sigblob, 's', len); pamsshagentauth_xfree(sigblob); pamsshagentauth_verbose("ssh_rsa_verify: signature %scorrect", (ret==0) ? "in" : ""); return ret; }
/* Modified slightly from original found in auth2-pubkey.c */ int pam_user_key_allowed2(struct passwd *pw, Key *key, char *file) { char line[SSH_MAX_PUBKEY_BYTES]; int found_key = 0; FILE *f; u_long linenum = 0; struct stat st; Key *found; char *fp; pamsshagentauth_verbose("trying public key file %s", file); /* Fail not so quietly if file does not exist */ if (stat(file, &st) < 0) { pamsshagentauth_verbose("File not found: %s", file); return 0; } /* Open the file containing the authorized keys. */ f = fopen(file, "r"); if (!f) { return 0; } if ( pamsshagentauth_secure_filename(f, file, pw, line, sizeof(line)) != 0) { fclose(f); pamsshagentauth_logit("Authentication refused: %s", line); return 0; } found_key = 0; found = pamsshagentauth_key_new(key->type); while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) { char *cp, *key_options = NULL; /* Skip leading whitespace, empty and comment lines. */ for (cp = line; *cp == ' ' || *cp == '\t'; cp++) ; if (!*cp || *cp == '\n' || *cp == '#') continue; if (pamsshagentauth_key_read(found, &cp) != 1) { /* no key? check if there are options for this key */ int quoted = 0; pamsshagentauth_verbose("user_key_allowed: check options: '%s'", cp); key_options = cp; for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) { if (*cp == '\\' && cp[1] == '"') cp++; /* Skip both */ else if (*cp == '"') quoted = !quoted; } /* Skip remaining whitespace. */ for (; *cp == ' ' || *cp == '\t'; cp++) ; if (pamsshagentauth_key_read(found, &cp) != 1) { pamsshagentauth_verbose("user_key_allowed: advance: '%s'", cp); /* still no key? advance to next line*/ continue; } } if (pamsshagentauth_key_equal(found, key)) { found_key = 1; pamsshagentauth_logit("matching key found: file %s, line %lu", file, linenum); fp = pamsshagentauth_key_fingerprint(found, SSH_FP_MD5, SSH_FP_HEX); pamsshagentauth_logit("Found matching %s key: %s", pamsshagentauth_key_type(found), fp); pamsshagentauth_xfree(fp); break; } } fclose(f); pamsshagentauth_key_free(found); if (!found_key) pamsshagentauth_verbose("key not found"); return found_key; }
int pamsshagentauth_tun_open(int tun, int mode) { #if defined(CUSTOM_SYS_TUN_OPEN) return (sys_tun_open(tun, mode)); #elif defined(SSH_TUN_OPENBSD) struct ifreq ifr; char name[100]; int fd = -1, sock; /* Open the tunnel device */ if (tun <= SSH_TUNID_MAX) { snprintf(name, sizeof(name), "/dev/tun%d", tun); fd = open(name, O_RDWR); } else if (tun == SSH_TUNID_ANY) { for (tun = 100; tun >= 0; tun--) { snprintf(name, sizeof(name), "/dev/tun%d", tun); if ((fd = open(name, O_RDWR)) >= 0) break; } } else { pamsshagentauth_verbose("%s: invalid tunnel %u", __func__, tun); return (-1); } if (fd < 0) { pamsshagentauth_verbose("%s: %s open failed: %s", __func__, name, strerror(errno)); return (-1); } pamsshagentauth_verbose("%s: %s mode %d fd %d", __func__, name, mode, fd); /* Set the tunnel device operation mode */ snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "tun%d", tun); if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) goto failed; if (ioctl(sock, SIOCGIFFLAGS, &ifr) == -1) goto failed; /* Set interface mode */ ifr.ifr_flags &= ~IFF_UP; if (mode == SSH_TUNMODE_ETHERNET) ifr.ifr_flags |= IFF_LINK0; else ifr.ifr_flags &= ~IFF_LINK0; if (ioctl(sock, SIOCSIFFLAGS, &ifr) == -1) goto failed; /* Bring interface up */ ifr.ifr_flags |= IFF_UP; if (ioctl(sock, SIOCSIFFLAGS, &ifr) == -1) goto failed; close(sock); return (fd); failed: if (fd >= 0) close(fd); if (sock >= 0) close(sock); pamsshagentauth_verbose("%s: failed to set %s mode %d: %s", __func__, name, mode, strerror(errno)); return (-1); #else UNUSED(tun); UNUSED(mode); pamsshagentauth_logerror("Tunnel interfaces are not supported on this platform"); return (-1); #endif }