version 1.7, 2020/01/23 07:10:22 |
version 1.8, 2020/10/18 11:32:02 |
|
|
start_helper(int *fdp, pid_t *pidp, void (**osigchldp)(int)) |
start_helper(int *fdp, pid_t *pidp, void (**osigchldp)(int)) |
{ |
{ |
void (*osigchld)(int); |
void (*osigchld)(int); |
int oerrno, pair[2], r = SSH_ERR_INTERNAL_ERROR; |
int oerrno, pair[2]; |
pid_t pid; |
pid_t pid; |
char *helper, *verbosity = NULL; |
char *helper, *verbosity = NULL; |
|
|
|
|
helper = _PATH_SSH_SK_HELPER; |
helper = _PATH_SSH_SK_HELPER; |
if (access(helper, X_OK) != 0) { |
if (access(helper, X_OK) != 0) { |
oerrno = errno; |
oerrno = errno; |
error("%s: helper \"%s\" unusable: %s", __func__, helper, |
error_f("helper \"%s\" unusable: %s", helper, strerror(errno)); |
strerror(errno)); |
|
errno = oerrno; |
errno = oerrno; |
return SSH_ERR_SYSTEM_ERROR; |
return SSH_ERR_SYSTEM_ERROR; |
} |
} |
|
|
if (pid == 0) { |
if (pid == 0) { |
if ((dup2(pair[1], STDIN_FILENO) == -1) || |
if ((dup2(pair[1], STDIN_FILENO) == -1) || |
(dup2(pair[1], STDOUT_FILENO) == -1)) { |
(dup2(pair[1], STDOUT_FILENO) == -1)) { |
error("%s: dup2: %s", __func__, ssh_err(r)); |
error_f("dup2: %s", strerror(errno)); |
_exit(1); |
_exit(1); |
} |
} |
close(pair[0]); |
close(pair[0]); |
close(pair[1]); |
close(pair[1]); |
closefrom(STDERR_FILENO + 1); |
closefrom(STDERR_FILENO + 1); |
debug("%s: starting %s %s", __func__, helper, |
debug_f("starting %s %s", helper, |
verbosity == NULL ? "" : verbosity); |
verbosity == NULL ? "" : verbosity); |
execlp(helper, helper, verbosity, (char *)NULL); |
execlp(helper, helper, verbosity, (char *)NULL); |
error("%s: execlp: %s", __func__, strerror(errno)); |
error_f("execlp: %s", strerror(errno)); |
_exit(1); |
_exit(1); |
} |
} |
close(pair[1]); |
close(pair[1]); |
|
|
/* success */ |
/* success */ |
debug3("%s: started pid=%ld", __func__, (long)pid); |
debug3_f("started pid=%ld", (long)pid); |
*fdp = pair[0]; |
*fdp = pair[0]; |
*pidp = pid; |
*pidp = pid; |
*osigchldp = osigchld; |
*osigchldp = osigchld; |
|
|
{ |
{ |
int status, oerrno; |
int status, oerrno; |
|
|
debug3("%s: pid=%ld", __func__, (long)pid); |
debug3_f("pid=%ld", (long)pid); |
|
|
errno = 0; |
errno = 0; |
while (waitpid(pid, &status, 0) == -1) { |
while (waitpid(pid, &status, 0) == -1) { |
|
|
continue; |
continue; |
} |
} |
oerrno = errno; |
oerrno = errno; |
error("%s: waitpid: %s", __func__, strerror(errno)); |
error_f("waitpid: %s", strerror(errno)); |
errno = oerrno; |
errno = oerrno; |
return SSH_ERR_SYSTEM_ERROR; |
return SSH_ERR_SYSTEM_ERROR; |
} |
} |
if (!WIFEXITED(status)) { |
if (!WIFEXITED(status)) { |
error("%s: helper exited abnormally", __func__); |
error_f("helper exited abnormally"); |
return SSH_ERR_AGENT_FAILURE; |
return SSH_ERR_AGENT_FAILURE; |
} else if (WEXITSTATUS(status) != 0) { |
} else if (WEXITSTATUS(status) != 0) { |
error("%s: helper exited with non-zero exit status", __func__); |
error_f("helper exited with non-zero exit status"); |
return SSH_ERR_AGENT_FAILURE; |
return SSH_ERR_AGENT_FAILURE; |
} |
} |
return 0; |
return 0; |
|
|
(r = sshbuf_put_u8(req, log_is_on_stderr() != 0)) != 0 || |
(r = sshbuf_put_u8(req, log_is_on_stderr() != 0)) != 0 || |
(r = sshbuf_put_u32(req, ll < 0 ? 0 : ll)) != 0 || |
(r = sshbuf_put_u32(req, ll < 0 ? 0 : ll)) != 0 || |
(r = sshbuf_putb(req, msg)) != 0) { |
(r = sshbuf_putb(req, msg)) != 0) { |
error("%s: build: %s", __func__, ssh_err(r)); |
error_fr(r, "compose"); |
goto out; |
goto out; |
} |
} |
if ((r = ssh_msg_send(fd, SSH_SK_HELPER_VERSION, req)) != 0) { |
if ((r = ssh_msg_send(fd, SSH_SK_HELPER_VERSION, req)) != 0) { |
error("%s: send: %s", __func__, ssh_err(r)); |
error_fr(r, "send"); |
goto out; |
goto out; |
} |
} |
if ((r = ssh_msg_recv(fd, resp)) != 0) { |
if ((r = ssh_msg_recv(fd, resp)) != 0) { |
error("%s: receive: %s", __func__, ssh_err(r)); |
error_fr(r, "receive"); |
goto out; |
goto out; |
} |
} |
if ((r = sshbuf_get_u8(resp, &version)) != 0) { |
if ((r = sshbuf_get_u8(resp, &version)) != 0) { |
error("%s: parse version: %s", __func__, ssh_err(r)); |
error_fr(r, "parse version"); |
goto out; |
goto out; |
} |
} |
if (version != SSH_SK_HELPER_VERSION) { |
if (version != SSH_SK_HELPER_VERSION) { |
error("%s: unsupported version: got %u, expected %u", |
error_f("unsupported version: got %u, expected %u", |
__func__, version, SSH_SK_HELPER_VERSION); |
version, SSH_SK_HELPER_VERSION); |
r = SSH_ERR_INVALID_FORMAT; |
r = SSH_ERR_INVALID_FORMAT; |
goto out; |
goto out; |
} |
} |
if ((r = sshbuf_get_u32(resp, &rtype)) != 0) { |
if ((r = sshbuf_get_u32(resp, &rtype)) != 0) { |
error("%s: parse message type: %s", __func__, ssh_err(r)); |
error_fr(r, "parse message type"); |
goto out; |
goto out; |
} |
} |
if (rtype == SSH_SK_HELPER_ERROR) { |
if (rtype == SSH_SK_HELPER_ERROR) { |
if ((r = sshbuf_get_u32(resp, &rerr)) != 0) { |
if ((r = sshbuf_get_u32(resp, &rerr)) != 0) { |
error("%s: parse error: %s", __func__, ssh_err(r)); |
error_fr(r, "parse"); |
goto out; |
goto out; |
} |
} |
debug("%s: helper returned error -%u", __func__, rerr); |
debug_f("helper returned error -%u", rerr); |
/* OpenSSH error values are negative; encoded as -err on wire */ |
/* OpenSSH error values are negative; encoded as -err on wire */ |
if (rerr == 0 || rerr >= INT_MAX) |
if (rerr == 0 || rerr >= INT_MAX) |
r = SSH_ERR_INTERNAL_ERROR; |
r = SSH_ERR_INTERNAL_ERROR; |
|
|
r = -(int)rerr; |
r = -(int)rerr; |
goto out; |
goto out; |
} else if (rtype != type) { |
} else if (rtype != type) { |
error("%s: helper returned incorrect message type %u, " |
error_f("helper returned incorrect message type %u, " |
"expecting %u", __func__, rtype, type); |
"expecting %u", rtype, type); |
r = SSH_ERR_INTERNAL_ERROR; |
r = SSH_ERR_INTERNAL_ERROR; |
goto out; |
goto out; |
} |
} |
|
|
} |
} |
|
|
if ((r = sshkey_private_serialize(key, kbuf)) != 0) { |
if ((r = sshkey_private_serialize(key, kbuf)) != 0) { |
error("%s: serialize private key: %s", __func__, ssh_err(r)); |
error_fr(r, "encode key"); |
goto out; |
goto out; |
} |
} |
if ((r = sshbuf_put_stringb(req, kbuf)) != 0 || |
if ((r = sshbuf_put_stringb(req, kbuf)) != 0 || |
|
|
(r = sshbuf_put_cstring(req, NULL)) != 0 || /* alg */ |
(r = sshbuf_put_cstring(req, NULL)) != 0 || /* alg */ |
(r = sshbuf_put_u32(req, compat)) != 0 || |
(r = sshbuf_put_u32(req, compat)) != 0 || |
(r = sshbuf_put_cstring(req, pin)) != 0) { |
(r = sshbuf_put_cstring(req, pin)) != 0) { |
error("%s: compose: %s", __func__, ssh_err(r)); |
error_fr(r, "compose"); |
goto out; |
goto out; |
} |
} |
|
|
if ((fp = sshkey_fingerprint(key, SSH_FP_HASH_DEFAULT, |
if ((fp = sshkey_fingerprint(key, SSH_FP_HASH_DEFAULT, |
SSH_FP_DEFAULT)) == NULL) { |
SSH_FP_DEFAULT)) == NULL) { |
error("%s: sshkey_fingerprint failed", __func__); |
error_f("sshkey_fingerprint failed"); |
r = SSH_ERR_ALLOC_FAIL; |
r = SSH_ERR_ALLOC_FAIL; |
goto out; |
goto out; |
} |
} |
|
|
goto out; |
goto out; |
|
|
if ((r = sshbuf_get_string(resp, sigp, lenp)) != 0) { |
if ((r = sshbuf_get_string(resp, sigp, lenp)) != 0) { |
error("%s: parse signature: %s", __func__, ssh_err(r)); |
error_fr(r, "parse signature"); |
r = SSH_ERR_INVALID_FORMAT; |
r = SSH_ERR_INVALID_FORMAT; |
goto out; |
goto out; |
} |
} |
if (sshbuf_len(resp) != 0) { |
if (sshbuf_len(resp) != 0) { |
error("%s: trailing data in response", __func__); |
error_f("trailing data in response"); |
r = SSH_ERR_INVALID_FORMAT; |
r = SSH_ERR_INVALID_FORMAT; |
goto out; |
goto out; |
} |
} |
|
|
(r = sshbuf_put_u8(req, flags)) != 0 || |
(r = sshbuf_put_u8(req, flags)) != 0 || |
(r = sshbuf_put_cstring(req, pin)) != 0 || |
(r = sshbuf_put_cstring(req, pin)) != 0 || |
(r = sshbuf_put_stringb(req, challenge_buf)) != 0) { |
(r = sshbuf_put_stringb(req, challenge_buf)) != 0) { |
error("%s: compose: %s", __func__, ssh_err(r)); |
error_fr(r, "compose"); |
goto out; |
goto out; |
} |
} |
|
|
|
|
|
|
if ((r = sshbuf_get_stringb(resp, kbuf)) != 0 || |
if ((r = sshbuf_get_stringb(resp, kbuf)) != 0 || |
(r = sshbuf_get_stringb(resp, abuf)) != 0) { |
(r = sshbuf_get_stringb(resp, abuf)) != 0) { |
error("%s: parse signature: %s", __func__, ssh_err(r)); |
error_fr(r, "parse"); |
r = SSH_ERR_INVALID_FORMAT; |
r = SSH_ERR_INVALID_FORMAT; |
goto out; |
goto out; |
} |
} |
if (sshbuf_len(resp) != 0) { |
if (sshbuf_len(resp) != 0) { |
error("%s: trailing data in response", __func__); |
error_f("trailing data in response"); |
r = SSH_ERR_INVALID_FORMAT; |
r = SSH_ERR_INVALID_FORMAT; |
goto out; |
goto out; |
} |
} |
if ((r = sshkey_private_deserialize(kbuf, &key)) != 0) { |
if ((r = sshkey_private_deserialize(kbuf, &key)) != 0) { |
error("Unable to parse private key: %s", ssh_err(r)); |
error_fr(r, "encode"); |
goto out; |
goto out; |
} |
} |
if (attest != NULL && (r = sshbuf_putb(attest, abuf)) != 0) { |
if (attest != NULL && (r = sshbuf_putb(attest, abuf)) != 0) { |
error("%s: buffer error: %s", __func__, ssh_err(r)); |
error_fr(r, "encode attestation information"); |
goto out; |
goto out; |
} |
} |
|
|
|
|
if ((r = sshbuf_put_cstring(req, provider_path)) != 0 || |
if ((r = sshbuf_put_cstring(req, provider_path)) != 0 || |
(r = sshbuf_put_cstring(req, device)) != 0 || |
(r = sshbuf_put_cstring(req, device)) != 0 || |
(r = sshbuf_put_cstring(req, pin)) != 0) { |
(r = sshbuf_put_cstring(req, pin)) != 0) { |
error("%s: compose: %s", __func__, ssh_err(r)); |
error_fr(r, "compose"); |
goto out; |
goto out; |
} |
} |
|
|
|
|
/* key, comment */ |
/* key, comment */ |
if ((r = sshbuf_get_stringb(resp, kbuf)) != 0 || |
if ((r = sshbuf_get_stringb(resp, kbuf)) != 0 || |
(r = sshbuf_get_cstring(resp, NULL, NULL)) != 0) { |
(r = sshbuf_get_cstring(resp, NULL, NULL)) != 0) { |
error("%s: parse signature: %s", __func__, ssh_err(r)); |
error_fr(r, "parse signature"); |
r = SSH_ERR_INVALID_FORMAT; |
r = SSH_ERR_INVALID_FORMAT; |
goto out; |
goto out; |
} |
} |
if ((r = sshkey_private_deserialize(kbuf, &key)) != 0) { |
if ((r = sshkey_private_deserialize(kbuf, &key)) != 0) { |
error("Unable to parse private key: %s", ssh_err(r)); |
error_fr(r, "decode key"); |
goto out; |
goto out; |
} |
} |
if ((tmp = recallocarray(keys, nkeys, nkeys + 1, |
if ((tmp = recallocarray(keys, nkeys, nkeys + 1, |
sizeof(*keys))) == NULL) { |
sizeof(*keys))) == NULL) { |
error("%s: recallocarray keys failed", __func__); |
error_f("recallocarray keys failed"); |
goto out; |
goto out; |
} |
} |
debug("%s: keys[%zu]: %s %s", __func__, |
debug_f("keys[%zu]: %s %s", nkeys, sshkey_type(key), |
nkeys, sshkey_type(key), key->sk_application); |
key->sk_application); |
keys = tmp; |
keys = tmp; |
keys[nkeys++] = key; |
keys[nkeys++] = key; |
key = NULL; |
key = NULL; |