Changeset View
Changeset View
Standalone View
Standalone View
authfile.c
/* $OpenBSD: authfile.c,v 1.131 2018/09/21 12:20:12 djm Exp $ */ | /* $OpenBSD: authfile.c,v 1.141 2020/06/18 23:33:38 djm Exp $ */ | ||||
/* | /* | ||||
* Copyright (c) 2000, 2013 Markus Friedl. All rights reserved. | * Copyright (c) 2000, 2013 Markus Friedl. All rights reserved. | ||||
* | * | ||||
* Redistribution and use in source and binary forms, with or without | * Redistribution and use in source and binary forms, with or without | ||||
* modification, are permitted provided that the following conditions | * modification, are permitted provided that the following conditions | ||||
* are met: | * are met: | ||||
* 1. Redistributions of source code must retain the above copyright | * 1. Redistributions of source code must retain the above copyright | ||||
* notice, this list of conditions and the following disclaimer. | * notice, this list of conditions and the following disclaimer. | ||||
Show All 40 Lines | |||||
#include "krl.h" | #include "krl.h" | ||||
#define MAX_KEY_FILE_SIZE (1024 * 1024) | #define MAX_KEY_FILE_SIZE (1024 * 1024) | ||||
/* Save a key blob to a file */ | /* Save a key blob to a file */ | ||||
static int | static int | ||||
sshkey_save_private_blob(struct sshbuf *keybuf, const char *filename) | sshkey_save_private_blob(struct sshbuf *keybuf, const char *filename) | ||||
{ | { | ||||
int fd, oerrno; | int r; | ||||
mode_t omask; | |||||
if ((fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0) | omask = umask(077); | ||||
return SSH_ERR_SYSTEM_ERROR; | r = sshbuf_write_file(filename, keybuf); | ||||
if (atomicio(vwrite, fd, sshbuf_mutable_ptr(keybuf), | umask(omask); | ||||
sshbuf_len(keybuf)) != sshbuf_len(keybuf)) { | return r; | ||||
oerrno = errno; | |||||
close(fd); | |||||
unlink(filename); | |||||
errno = oerrno; | |||||
return SSH_ERR_SYSTEM_ERROR; | |||||
} | } | ||||
close(fd); | |||||
return 0; | |||||
} | |||||
int | int | ||||
sshkey_save_private(struct sshkey *key, const char *filename, | sshkey_save_private(struct sshkey *key, const char *filename, | ||||
const char *passphrase, const char *comment, | const char *passphrase, const char *comment, | ||||
int force_new_format, const char *new_format_cipher, int new_format_rounds) | int format, const char *openssh_format_cipher, int openssh_format_rounds) | ||||
{ | { | ||||
struct sshbuf *keyblob = NULL; | struct sshbuf *keyblob = NULL; | ||||
int r; | int r; | ||||
if ((keyblob = sshbuf_new()) == NULL) | if ((keyblob = sshbuf_new()) == NULL) | ||||
return SSH_ERR_ALLOC_FAIL; | return SSH_ERR_ALLOC_FAIL; | ||||
if ((r = sshkey_private_to_fileblob(key, keyblob, passphrase, comment, | if ((r = sshkey_private_to_fileblob(key, keyblob, passphrase, comment, | ||||
force_new_format, new_format_cipher, new_format_rounds)) != 0) | format, openssh_format_cipher, openssh_format_rounds)) != 0) | ||||
goto out; | goto out; | ||||
if ((r = sshkey_save_private_blob(keyblob, filename)) != 0) | if ((r = sshkey_save_private_blob(keyblob, filename)) != 0) | ||||
goto out; | goto out; | ||||
r = 0; | r = 0; | ||||
out: | out: | ||||
sshbuf_free(keyblob); | sshbuf_free(keyblob); | ||||
return r; | return r; | ||||
} | } | ||||
/* Load a key from a fd into a buffer */ | |||||
int | |||||
sshkey_load_file(int fd, struct sshbuf *blob) | |||||
{ | |||||
u_char buf[1024]; | |||||
size_t len; | |||||
struct stat st; | |||||
int r; | |||||
if (fstat(fd, &st) < 0) | |||||
return SSH_ERR_SYSTEM_ERROR; | |||||
if ((st.st_mode & (S_IFSOCK|S_IFCHR|S_IFIFO)) == 0 && | |||||
st.st_size > MAX_KEY_FILE_SIZE) | |||||
return SSH_ERR_INVALID_FORMAT; | |||||
for (;;) { | |||||
if ((len = atomicio(read, fd, buf, sizeof(buf))) == 0) { | |||||
if (errno == EPIPE) | |||||
break; | |||||
r = SSH_ERR_SYSTEM_ERROR; | |||||
goto out; | |||||
} | |||||
if ((r = sshbuf_put(blob, buf, len)) != 0) | |||||
goto out; | |||||
if (sshbuf_len(blob) > MAX_KEY_FILE_SIZE) { | |||||
r = SSH_ERR_INVALID_FORMAT; | |||||
goto out; | |||||
} | |||||
} | |||||
if ((st.st_mode & (S_IFSOCK|S_IFCHR|S_IFIFO)) == 0 && | |||||
st.st_size != (off_t)sshbuf_len(blob)) { | |||||
r = SSH_ERR_FILE_CHANGED; | |||||
goto out; | |||||
} | |||||
r = 0; | |||||
out: | |||||
explicit_bzero(buf, sizeof(buf)); | |||||
if (r != 0) | |||||
sshbuf_reset(blob); | |||||
return r; | |||||
} | |||||
/* XXX remove error() calls from here? */ | /* XXX remove error() calls from here? */ | ||||
int | int | ||||
sshkey_perm_ok(int fd, const char *filename) | sshkey_perm_ok(int fd, const char *filename) | ||||
{ | { | ||||
struct stat st; | struct stat st; | ||||
if (fstat(fd, &st) < 0) | if (fstat(fd, &st) == -1) | ||||
return SSH_ERR_SYSTEM_ERROR; | return SSH_ERR_SYSTEM_ERROR; | ||||
/* | /* | ||||
* if a key owned by the user is accessed, then we check the | * if a key owned by the user is accessed, then we check the | ||||
* permissions of the file. if the key owned by a different user, | * permissions of the file. if the key owned by a different user, | ||||
* then we don't care. | * then we don't care. | ||||
*/ | */ | ||||
#ifdef HAVE_CYGWIN | #ifdef HAVE_CYGWIN | ||||
if (check_ntsec(filename)) | if (check_ntsec(filename)) | ||||
#endif | #endif | ||||
if ((st.st_uid == getuid()) && (st.st_mode & 077) != 0) { | if ((st.st_uid == getuid()) && (st.st_mode & 077) != 0) { | ||||
error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); | error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); | ||||
error("@ WARNING: UNPROTECTED PRIVATE KEY FILE! @"); | error("@ WARNING: UNPROTECTED PRIVATE KEY FILE! @"); | ||||
error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); | error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); | ||||
error("Permissions 0%3.3o for '%s' are too open.", | error("Permissions 0%3.3o for '%s' are too open.", | ||||
(u_int)st.st_mode & 0777, filename); | (u_int)st.st_mode & 0777, filename); | ||||
error("It is required that your private key files are NOT accessible by others."); | error("It is required that your private key files are NOT accessible by others."); | ||||
error("This private key will be ignored."); | error("This private key will be ignored."); | ||||
return SSH_ERR_KEY_BAD_PERMISSIONS; | return SSH_ERR_KEY_BAD_PERMISSIONS; | ||||
} | } | ||||
return 0; | return 0; | ||||
} | } | ||||
/* XXX kill perm_ok now that we have SSH_ERR_KEY_BAD_PERMISSIONS? */ | |||||
int | int | ||||
sshkey_load_private_type(int type, const char *filename, const char *passphrase, | sshkey_load_private_type(int type, const char *filename, const char *passphrase, | ||||
struct sshkey **keyp, char **commentp, int *perm_ok) | struct sshkey **keyp, char **commentp) | ||||
{ | { | ||||
int fd, r; | int fd, r; | ||||
if (keyp != NULL) | if (keyp != NULL) | ||||
*keyp = NULL; | *keyp = NULL; | ||||
if (commentp != NULL) | if (commentp != NULL) | ||||
*commentp = NULL; | *commentp = NULL; | ||||
if ((fd = open(filename, O_RDONLY)) < 0) { | if ((fd = open(filename, O_RDONLY)) == -1) | ||||
if (perm_ok != NULL) | |||||
*perm_ok = 0; | |||||
return SSH_ERR_SYSTEM_ERROR; | return SSH_ERR_SYSTEM_ERROR; | ||||
} | |||||
if (sshkey_perm_ok(fd, filename) != 0) { | r = sshkey_perm_ok(fd, filename); | ||||
if (perm_ok != NULL) | if (r != 0) | ||||
*perm_ok = 0; | |||||
r = SSH_ERR_KEY_BAD_PERMISSIONS; | |||||
goto out; | goto out; | ||||
} | |||||
if (perm_ok != NULL) | |||||
*perm_ok = 1; | |||||
r = sshkey_load_private_type_fd(fd, type, passphrase, keyp, commentp); | r = sshkey_load_private_type_fd(fd, type, passphrase, keyp, commentp); | ||||
if (r == 0 && keyp && *keyp) | if (r == 0 && keyp && *keyp) | ||||
r = sshkey_set_filename(*keyp, filename); | r = sshkey_set_filename(*keyp, filename); | ||||
out: | out: | ||||
close(fd); | close(fd); | ||||
return r; | return r; | ||||
} | } | ||||
int | int | ||||
sshkey_load_private(const char *filename, const char *passphrase, | |||||
struct sshkey **keyp, char **commentp) | |||||
{ | |||||
return sshkey_load_private_type(KEY_UNSPEC, filename, passphrase, | |||||
keyp, commentp); | |||||
} | |||||
int | |||||
sshkey_load_private_type_fd(int fd, int type, const char *passphrase, | sshkey_load_private_type_fd(int fd, int type, const char *passphrase, | ||||
struct sshkey **keyp, char **commentp) | struct sshkey **keyp, char **commentp) | ||||
{ | { | ||||
struct sshbuf *buffer = NULL; | struct sshbuf *buffer = NULL; | ||||
int r; | int r; | ||||
if (keyp != NULL) | if (keyp != NULL) | ||||
*keyp = NULL; | *keyp = NULL; | ||||
if ((buffer = sshbuf_new()) == NULL) { | if ((r = sshbuf_load_fd(fd, &buffer)) != 0 || | ||||
r = SSH_ERR_ALLOC_FAIL; | |||||
goto out; | |||||
} | |||||
if ((r = sshkey_load_file(fd, buffer)) != 0 || | |||||
(r = sshkey_parse_private_fileblob_type(buffer, type, | (r = sshkey_parse_private_fileblob_type(buffer, type, | ||||
passphrase, keyp, commentp)) != 0) | passphrase, keyp, commentp)) != 0) | ||||
goto out; | goto out; | ||||
/* success */ | /* success */ | ||||
r = 0; | r = 0; | ||||
out: | out: | ||||
sshbuf_free(buffer); | sshbuf_free(buffer); | ||||
return r; | return r; | ||||
} | } | ||||
/* XXX this is almost identical to sshkey_load_private_type() */ | /* Load a pubkey from the unencrypted envelope of a new-format private key */ | ||||
int | static int | ||||
sshkey_load_private(const char *filename, const char *passphrase, | sshkey_load_pubkey_from_private(const char *filename, struct sshkey **pubkeyp) | ||||
struct sshkey **keyp, char **commentp) | |||||
{ | { | ||||
struct sshbuf *buffer = NULL; | struct sshbuf *buffer = NULL; | ||||
struct sshkey *pubkey = NULL; | |||||
int r, fd; | int r, fd; | ||||
if (keyp != NULL) | if (pubkeyp != NULL) | ||||
*keyp = NULL; | *pubkeyp = NULL; | ||||
if (commentp != NULL) | |||||
*commentp = NULL; | |||||
if ((fd = open(filename, O_RDONLY)) < 0) | if ((fd = open(filename, O_RDONLY)) == -1) | ||||
return SSH_ERR_SYSTEM_ERROR; | return SSH_ERR_SYSTEM_ERROR; | ||||
if (sshkey_perm_ok(fd, filename) != 0) { | if ((r = sshbuf_load_fd(fd, &buffer)) != 0 || | ||||
r = SSH_ERR_KEY_BAD_PERMISSIONS; | (r = sshkey_parse_pubkey_from_private_fileblob_type(buffer, | ||||
KEY_UNSPEC, &pubkey)) != 0) | |||||
goto out; | goto out; | ||||
} | if ((r = sshkey_set_filename(pubkey, filename)) != 0) | ||||
if ((buffer = sshbuf_new()) == NULL) { | |||||
r = SSH_ERR_ALLOC_FAIL; | |||||
goto out; | goto out; | ||||
/* success */ | |||||
if (pubkeyp != NULL) { | |||||
*pubkeyp = pubkey; | |||||
pubkey = NULL; | |||||
} | } | ||||
if ((r = sshkey_load_file(fd, buffer)) != 0 || | |||||
(r = sshkey_parse_private_fileblob(buffer, passphrase, keyp, | |||||
commentp)) != 0) | |||||
goto out; | |||||
if (keyp && *keyp && | |||||
(r = sshkey_set_filename(*keyp, filename)) != 0) | |||||
goto out; | |||||
r = 0; | r = 0; | ||||
out: | out: | ||||
close(fd); | close(fd); | ||||
sshbuf_free(buffer); | sshbuf_free(buffer); | ||||
sshkey_free(pubkey); | |||||
return r; | return r; | ||||
} | } | ||||
static int | static int | ||||
sshkey_try_load_public(struct sshkey *k, const char *filename, char **commentp) | sshkey_try_load_public(struct sshkey **kp, const char *filename, | ||||
char **commentp) | |||||
{ | { | ||||
FILE *f; | FILE *f; | ||||
char *line = NULL, *cp; | char *line = NULL, *cp; | ||||
size_t linesize = 0; | size_t linesize = 0; | ||||
int r; | int r; | ||||
struct sshkey *k = NULL; | |||||
*kp = NULL; | |||||
if (commentp != NULL) | if (commentp != NULL) | ||||
*commentp = NULL; | *commentp = NULL; | ||||
if ((f = fopen(filename, "r")) == NULL) | if ((f = fopen(filename, "r")) == NULL) | ||||
return SSH_ERR_SYSTEM_ERROR; | return SSH_ERR_SYSTEM_ERROR; | ||||
if ((k = sshkey_new(KEY_UNSPEC)) == NULL) { | |||||
fclose(f); | |||||
return SSH_ERR_ALLOC_FAIL; | |||||
} | |||||
while (getline(&line, &linesize, f) != -1) { | while (getline(&line, &linesize, f) != -1) { | ||||
cp = line; | cp = line; | ||||
switch (*cp) { | switch (*cp) { | ||||
case '#': | case '#': | ||||
case '\n': | case '\n': | ||||
case '\0': | case '\0': | ||||
continue; | continue; | ||||
} | } | ||||
/* Abort loading if this looks like a private key */ | /* Abort loading if this looks like a private key */ | ||||
if (strncmp(cp, "-----BEGIN", 10) == 0 || | if (strncmp(cp, "-----BEGIN", 10) == 0 || | ||||
strcmp(cp, "SSH PRIVATE KEY FILE") == 0) | strcmp(cp, "SSH PRIVATE KEY FILE") == 0) | ||||
break; | break; | ||||
/* Skip leading whitespace. */ | /* Skip leading whitespace. */ | ||||
for (; *cp && (*cp == ' ' || *cp == '\t'); cp++) | for (; *cp && (*cp == ' ' || *cp == '\t'); cp++) | ||||
; | ; | ||||
if (*cp) { | if (*cp) { | ||||
if ((r = sshkey_read(k, &cp)) == 0) { | if ((r = sshkey_read(k, &cp)) == 0) { | ||||
cp[strcspn(cp, "\r\n")] = '\0'; | cp[strcspn(cp, "\r\n")] = '\0'; | ||||
if (commentp) { | if (commentp) { | ||||
*commentp = strdup(*cp ? | *commentp = strdup(*cp ? | ||||
cp : filename); | cp : filename); | ||||
if (*commentp == NULL) | if (*commentp == NULL) | ||||
r = SSH_ERR_ALLOC_FAIL; | r = SSH_ERR_ALLOC_FAIL; | ||||
} | } | ||||
/* success */ | |||||
*kp = k; | |||||
free(line); | free(line); | ||||
fclose(f); | fclose(f); | ||||
return r; | return r; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
free(k); | |||||
free(line); | free(line); | ||||
fclose(f); | fclose(f); | ||||
return SSH_ERR_INVALID_FORMAT; | return SSH_ERR_INVALID_FORMAT; | ||||
} | } | ||||
/* load public key from any pubkey file */ | /* load public key from any pubkey file */ | ||||
int | int | ||||
sshkey_load_public(const char *filename, struct sshkey **keyp, char **commentp) | sshkey_load_public(const char *filename, struct sshkey **keyp, char **commentp) | ||||
{ | { | ||||
struct sshkey *pub = NULL; | char *pubfile = NULL; | ||||
char *file = NULL; | int r, oerrno; | ||||
int r; | |||||
if (keyp != NULL) | if (keyp != NULL) | ||||
*keyp = NULL; | *keyp = NULL; | ||||
if (commentp != NULL) | if (commentp != NULL) | ||||
*commentp = NULL; | *commentp = NULL; | ||||
if ((pub = sshkey_new(KEY_UNSPEC)) == NULL) | if ((r = sshkey_try_load_public(keyp, filename, commentp)) == 0) | ||||
return SSH_ERR_ALLOC_FAIL; | |||||
if ((r = sshkey_try_load_public(pub, filename, commentp)) == 0) { | |||||
if (keyp != NULL) { | |||||
*keyp = pub; | |||||
pub = NULL; | |||||
} | |||||
r = 0; | |||||
goto out; | goto out; | ||||
} | |||||
sshkey_free(pub); | |||||
/* try .pub suffix */ | /* try .pub suffix */ | ||||
if (asprintf(&file, "%s.pub", filename) == -1) | if (asprintf(&pubfile, "%s.pub", filename) == -1) | ||||
return SSH_ERR_ALLOC_FAIL; | return SSH_ERR_ALLOC_FAIL; | ||||
if ((pub = sshkey_new(KEY_UNSPEC)) == NULL) { | if ((r = sshkey_try_load_public(keyp, pubfile, commentp)) == 0) | ||||
r = SSH_ERR_ALLOC_FAIL; | |||||
goto out; | goto out; | ||||
} | |||||
if ((r = sshkey_try_load_public(pub, file, commentp)) == 0) { | /* finally, try to extract public key from private key file */ | ||||
if (keyp != NULL) { | if ((r = sshkey_load_pubkey_from_private(filename, keyp)) == 0) | ||||
*keyp = pub; | goto out; | ||||
pub = NULL; | |||||
} | /* Pretend we couldn't find the key */ | ||||
r = 0; | r = SSH_ERR_SYSTEM_ERROR; | ||||
} | errno = ENOENT; | ||||
out: | out: | ||||
free(file); | oerrno = errno; | ||||
sshkey_free(pub); | free(pubfile); | ||||
errno = oerrno; | |||||
return r; | return r; | ||||
} | } | ||||
/* Load the certificate associated with the named private key */ | /* Load the certificate associated with the named private key */ | ||||
int | int | ||||
sshkey_load_cert(const char *filename, struct sshkey **keyp) | sshkey_load_cert(const char *filename, struct sshkey **keyp) | ||||
{ | { | ||||
struct sshkey *pub = NULL; | struct sshkey *pub = NULL; | ||||
char *file = NULL; | char *file = NULL; | ||||
int r = SSH_ERR_INTERNAL_ERROR; | int r = SSH_ERR_INTERNAL_ERROR; | ||||
if (keyp != NULL) | if (keyp != NULL) | ||||
*keyp = NULL; | *keyp = NULL; | ||||
if (asprintf(&file, "%s-cert.pub", filename) == -1) | if (asprintf(&file, "%s-cert.pub", filename) == -1) | ||||
return SSH_ERR_ALLOC_FAIL; | return SSH_ERR_ALLOC_FAIL; | ||||
if ((pub = sshkey_new(KEY_UNSPEC)) == NULL) { | r = sshkey_try_load_public(keyp, file, NULL); | ||||
goto out; | |||||
} | |||||
if ((r = sshkey_try_load_public(pub, file, NULL)) != 0) | |||||
goto out; | |||||
/* success */ | |||||
if (keyp != NULL) { | |||||
*keyp = pub; | |||||
pub = NULL; | |||||
} | |||||
r = 0; | |||||
out: | |||||
free(file); | free(file); | ||||
sshkey_free(pub); | sshkey_free(pub); | ||||
return r; | return r; | ||||
} | } | ||||
/* Load private key and certificate */ | /* Load private key and certificate */ | ||||
int | int | ||||
sshkey_load_private_cert(int type, const char *filename, const char *passphrase, | sshkey_load_private_cert(int type, const char *filename, const char *passphrase, | ||||
struct sshkey **keyp, int *perm_ok) | struct sshkey **keyp) | ||||
{ | { | ||||
struct sshkey *key = NULL, *cert = NULL; | struct sshkey *key = NULL, *cert = NULL; | ||||
int r; | int r; | ||||
if (keyp != NULL) | if (keyp != NULL) | ||||
*keyp = NULL; | *keyp = NULL; | ||||
switch (type) { | switch (type) { | ||||
#ifdef WITH_OPENSSL | #ifdef WITH_OPENSSL | ||||
case KEY_RSA: | case KEY_RSA: | ||||
case KEY_DSA: | case KEY_DSA: | ||||
case KEY_ECDSA: | case KEY_ECDSA: | ||||
#endif /* WITH_OPENSSL */ | #endif /* WITH_OPENSSL */ | ||||
case KEY_ED25519: | case KEY_ED25519: | ||||
case KEY_XMSS: | case KEY_XMSS: | ||||
case KEY_UNSPEC: | case KEY_UNSPEC: | ||||
break; | break; | ||||
default: | default: | ||||
return SSH_ERR_KEY_TYPE_UNKNOWN; | return SSH_ERR_KEY_TYPE_UNKNOWN; | ||||
} | } | ||||
if ((r = sshkey_load_private_type(type, filename, | if ((r = sshkey_load_private_type(type, filename, | ||||
passphrase, &key, NULL, perm_ok)) != 0 || | passphrase, &key, NULL)) != 0 || | ||||
(r = sshkey_load_cert(filename, &cert)) != 0) | (r = sshkey_load_cert(filename, &cert)) != 0) | ||||
goto out; | goto out; | ||||
/* Make sure the private key matches the certificate */ | /* Make sure the private key matches the certificate */ | ||||
if (sshkey_equal_public(key, cert) == 0) { | if (sshkey_equal_public(key, cert) == 0) { | ||||
r = SSH_ERR_KEY_CERT_MISMATCH; | r = SSH_ERR_KEY_CERT_MISMATCH; | ||||
goto out; | goto out; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 109 Lines • ▼ Show 20 Lines | case SSH_ERR_KEY_NOT_FOUND: | ||||
/* Key not found => not revoked */ | /* Key not found => not revoked */ | ||||
return 0; | return 0; | ||||
default: | default: | ||||
/* Some other error occurred */ | /* Some other error occurred */ | ||||
return r; | return r; | ||||
} | } | ||||
} | } | ||||
/* | |||||
* Advanced *cpp past the end of key options, defined as the first unquoted | |||||
* whitespace character. Returns 0 on success or -1 on failure (e.g. | |||||
* unterminated quotes). | |||||
*/ | |||||
int | |||||
sshkey_advance_past_options(char **cpp) | |||||
{ | |||||
char *cp = *cpp; | |||||
int quoted = 0; | |||||
for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) { | |||||
if (*cp == '\\' && cp[1] == '"') | |||||
cp++; /* Skip both */ | |||||
else if (*cp == '"') | |||||
quoted = !quoted; | |||||
} | |||||
*cpp = cp; | |||||
/* return failure for unterminated quotes */ | |||||
return (*cp == '\0' && quoted) ? -1 : 0; | |||||
} | |||||
/* Save a public key */ | |||||
int | |||||
sshkey_save_public(const struct sshkey *key, const char *path, | |||||
const char *comment) | |||||
{ | |||||
int fd, oerrno; | |||||
FILE *f = NULL; | |||||
int r = SSH_ERR_INTERNAL_ERROR; | |||||
if ((fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1) | |||||
return SSH_ERR_SYSTEM_ERROR; | |||||
if ((f = fdopen(fd, "w")) == NULL) { | |||||
r = SSH_ERR_SYSTEM_ERROR; | |||||
goto fail; | |||||
} | |||||
if ((r = sshkey_write(key, f)) != 0) | |||||
goto fail; | |||||
fprintf(f, " %s\n", comment); | |||||
if (ferror(f) || fclose(f) != 0) { | |||||
r = SSH_ERR_SYSTEM_ERROR; | |||||
fail: | |||||
oerrno = errno; | |||||
if (f != NULL) | |||||
fclose(f); | |||||
else | |||||
close(fd); | |||||
errno = oerrno; | |||||
return r; | |||||
} | |||||
return 0; | |||||
} |