Bugzilla – Attachment 2694 Details for
Bug 2436
Add ssh option to present certificates on command line
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
ssh-certificate-agent proof-of-concept code
ssh-certificate-agent.c (text/plain), 26.75 KB, created by
David Gervais
on 2015-09-04 22:26:48 AEST
(
hide
)
Description:
ssh-certificate-agent proof-of-concept code
Filename:
MIME Type:
Creator:
David Gervais
Created:
2015-09-04 22:26:48 AEST
Size:
26.75 KB
patch
obsolete
>/* ssh-certificate-agent.c > * Author: Meghana Bhat <mbhat@akamai.com> > * > * Modified version of: > * OpenBSD: ssh-agent.c by Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland > */ > >#include "includes.h" > >#include <sys/param.h> /* MIN MAX */ >#include <sys/types.h> >#include <sys/param.h> >#include <sys/resource.h> >#include <sys/stat.h> >#include <sys/socket.h> >#ifdef HAVE_SYS_TIME_H ># include <sys/time.h> >#endif >#ifdef HAVE_SYS_UN_H ># include <sys/un.h> >#endif >#include "openbsd-compat/sys-queue.h" > >#ifdef WITH_OPENSSL >#include <openssl/evp.h> >#include "openbsd-compat/openssl-compat.h" >#endif > >#include <errno.h> >#include <fcntl.h> >#include <limits.h> >#ifdef HAVE_PATHS_H ># include <paths.h> >#endif >#include <signal.h> >#include <stdarg.h> >#include <stdio.h> >#include <stdlib.h> >#include <time.h> >#include <string.h> >#include <unistd.h> >#ifdef HAVE_UTIL_H ># include <util.h> >#endif > >#include "key.h" /* XXX for typedef */ >#include "buffer.h" /* XXX for typedef */ > >#include "xmalloc.h" >#include "ssh.h" >#include "rsa.h" >#include "sshbuf.h" >#include "sshkey.h" >#include "authfd.h" >#include "authfile.h" >#include "compat.h" >#include "log.h" >#include "misc.h" >#include "digest.h" >#include "ssherr.h" > >#ifdef ENABLE_PKCS11 >#include "ssh-pkcs11.h" >#endif > >#if defined(HAVE_SYS_PRCTL_H) >#include <sys/prctl.h> /* For prctl() and PR_SET_DUMPABLE */ >#endif > >typedef enum { > AUTH_UNUSED, > AUTH_SOCKET, > AUTH_CONNECTION >} sock_type; > >typedef struct { > int fd; > sock_type type; > struct sshbuf *input; > struct sshbuf *output; > struct sshbuf *request; >} SocketEntry; > >u_int sockets_alloc = 0; >SocketEntry *sockets = NULL; > >typedef struct cert_identity { > TAILQ_ENTRY(cert_identity) next; > struct sshkey *key; > char *comment; >} CertificateId; > >typedef struct { > int nentries; > TAILQ_HEAD(idqueue, cert_identity) idlist; >} Idtab; > >/* certificates being used by agent */ >#define AGENT_MAX_CERTIFICATE_FILES 5 >Idtab idtable; >char *certificate_files[AGENT_MAX_CERTIFICATE_FILES]; >int num_certificate_files = 0; > >int max_fd = 0; > >/* pid of shell == parent of agent */ >pid_t parent_pid = -1; >time_t parent_alive_interval = 0; > >/* pid of process for which cleanup_socket is applicable */ >pid_t cleanup_pid = 0; > >/* pathname and directory for AUTH_SOCKET */ >char socket_name[PATH_MAX]; >char socket_dir[PATH_MAX]; > >/* locking */ >#define LOCK_SIZE 32 >#define LOCK_SALT_SIZE 16 >#define LOCK_ROUNDS 1 >int locked = 0; >char lock_passwd[LOCK_SIZE]; >char lock_salt[LOCK_SALT_SIZE]; > >extern char *__progname; > >/* Default lifetime in seconds (0 == forever) */ >static long lifetime = 0; > >static int fingerprint_hash = SSH_FP_HASH_DEFAULT; > >static void >close_socket(SocketEntry *e) >{ > close(e->fd); > e->fd = -1; > e->type = AUTH_UNUSED; > sshbuf_free(e->input); > sshbuf_free(e->output); > sshbuf_free(e->request); >} > >static void >idtab_init(void) >{ > TAILQ_INIT(&idtable.idlist); > idtable.nentries = 0; >} > >/* add certificate to list to be loaded */ >static void >add_agent_certificate(const char *filename) >{ > char *path; > int i; > > if (num_certificate_files >= AGENT_MAX_CERTIFICATE_FILES) > fatal("Too many certificate files specified (max %d)", > AGENT_MAX_CERTIFICATE_FILES); > > path = xstrdup(filename); > for (i = 0; i < num_certificate_files; i++) { > if (strcmp(certificate_files[i], path) == 0) { > debug("%s: ignoring duplicate certificate %s", __func__, path); > free(path); > return; > } > } > > certificate_files[num_certificate_files++] = path; >} > >/* set up the certificate for the agent */ >static void >load_agent_certificates(void) >{ > Idtab *tab = &idtable; > int fd, i, r = -1; > struct sshkey *cert; > char *comment; > > for (i = 0; i < num_certificate_files; i++) { > CertificateId *id; > > if (strcmp(certificate_files[i], "-") == 0) { > fd = STDIN_FILENO; > certificate_files[i] = "(stdin)"; > } else if ((fd = open(certificate_files[i], O_RDONLY)) < 0) { > perror(certificate_files[i]); > fprintf(stderr, "Failed to load certificate \"%s\"", > certificate_files[i]); > free(certificate_files[i]); > continue; > } > comment = xstrdup(certificate_files[i]); > if ((cert = sshkey_new(KEY_UNSPEC)) == NULL) > fatal("%s: memory allocation error", __func__); > > if ((r = sshkey_load_public_fd(cert, fd, NULL)) != 0) > fatal("%s: failed to load certificate.", __func__); > > if (cert == NULL || !key_is_cert(cert)) { > fprintf(stderr, "File path provided is not a certificate."); > key_free(cert); > free(comment); > continue; > } > > id = xcalloc(1, sizeof(CertificateId)); > id->key = cert; > id->comment = comment; > TAILQ_INSERT_TAIL(&tab->idlist, id, next); > tab->nentries++; > > if (i >= AGENT_MAX_CERTIFICATE_FILES) > break; > } >} > >/* return private key from given idlist that matches given cert */ >static struct sshkey * >lookup_identity_cert(struct sshkey *key, int version, struct ssh_identitylist *idlist) >{ > size_t i; > if (version != 2) > fatal("Internal error, bad protocol version %d", version); > if (idlist == NULL) > return (NULL); > for (i = 0; i < idlist->nkeys; i++) { > if (sshkey_equal_public(key, idlist->keys[i])) > return (idlist->keys[i]); > } > return (NULL); >} > >static void >send_status(SocketEntry *e, int success) >{ > int r; > > if ((r = sshbuf_put_u32(e->output, 1)) != 0 || > (r = sshbuf_put_u8(e->output, success ? > SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE)) != 0) > fatal("%s: buffer error: %s", __func__, ssh_err(r)); >} > >/* return failure message */ >static void >process_fail(SocketEntry *e) >{ > struct sshbuf *msg; > int r; > if ((msg = sshbuf_new()) == NULL) > fatal("%s: sshbuf_new failed", __func__); > if ((r = sshbuf_put_u8(msg, SSH_AGENT_FAILURE)) != 0) > fatal("%s: buffer error: %s", __func__, ssh_err(r)); > if ((r = sshbuf_put_stringb(e->output, msg)) != 0) > fatal("%s: buffer error: %s", __func__, ssh_err(r)); > sshbuf_free(msg); >} > >/* send certificate to 'client' if ssh2, nothing if ssh1 */ >static void >process_request_identities(SocketEntry *e, int version) >{ > struct sshbuf *msg; > int r; > Idtab *tab = &idtable; > CertificateId *id; > > if ((msg = sshbuf_new()) == NULL) > fatal("%s: sshbuf_new failed", __func__); > > int nentries = (version == 1) ? 0 : tab->nentries; > if ((r = sshbuf_put_u8(msg, (version == 1) ? > SSH_AGENT_RSA_IDENTITIES_ANSWER : > SSH2_AGENT_IDENTITIES_ANSWER)) != 0 || > (r = sshbuf_put_u32(msg, nentries)) != 0) > fatal("%s: buffer error: %s", __func__, ssh_err(r)); > if (version == 2) { > TAILQ_FOREACH(id, &tab->idlist, next) { > u_char *blob; > size_t blen; > > if ((r = sshkey_to_blob(id->key, &blob, &blen)) != 0) { > error("%s: sshkey_to_blob: %s", __func__, > ssh_err(r)); > } > if ((r = sshbuf_put_string(msg, blob, blen)) != 0) > fatal("%s: buffer error: %s", > __func__, ssh_err(r)); > free(blob); > if ((r = sshbuf_put_cstring(msg, id->comment)) != 0) > fatal("%s: buffer error: %s", __func__, ssh_err(r)); > } > } > if ((r = sshbuf_put_stringb(e->output, msg)) != 0) > fatal("%s: buffer error: %s", __func__, ssh_err(r)); > sshbuf_free(msg); >} > >#ifdef WITH_SSH1 >/* ssh1 only */ >static void >process_authentication_challenge1(SocketEntry *e) >{ > fatal("Certificate agent not supported on ssh1."); > process_fail(e); >} >#endif > >/* ssh2 only */ >static void >process_sign_request_v2(SocketEntry *e) >{ > u_char *blob, *data, *signature = NULL; > size_t blen, dlen, slen = 0; > u_int flags; > int r, ok = -1, agent_fd; > struct sshbuf *msg, *reply; > struct sshkey *key, *match; > struct ssh_identitylist *idlist; > u_char type; > > if ((msg = sshbuf_new()) == NULL) > fatal("%s: sshbuf_new failed", __func__); > if ((reply = sshbuf_new()) == NULL) > fatal("%s: sshbuf_new failed", __func__); > if ((r = sshbuf_get_string(e->request, &blob, &blen)) != 0 || > (r = sshbuf_get_string(e->request, &data, &dlen)) != 0 || > (r = sshbuf_get_u32(e->request, &flags)) != 0) > fatal("%s: buffer error: %s", __func__, ssh_err(r)); > if ((r = sshkey_from_blob(blob, blen, &key)) != 0) { > error("%s: cannot parse key blob: %s", __func__, ssh_err(ok)); > goto send2; > } > > /* First, get a connection to the authentication agent. */ > switch (r = ssh_get_authentication_socket(&agent_fd)) { > case SSH_ERR_SUCCESS: > break; > case SSH_ERR_AGENT_NOT_PRESENT: > debug("Couldn't open connection to agent"); > goto send2; > default: > debug("Error connecting to agent"); > goto send2; > } > > //fetch identities > if ((r = ssh_fetch_identitylist(agent_fd, 2, > &idlist)) != 0) { > if (r != SSH_ERR_AGENT_NO_IDENTITIES) { > debug("Error fetching identities."); > } > goto send; > } > if ((match = lookup_identity_cert(key, 2, idlist)) == NULL) { > verbose("%s: %s key not found", __func__, sshkey_type(key)); > goto send; > } > //now get the ssh agent to sign it > if (match->type != KEY_RSA) { > debug("Error, bad private key type."); > goto send; > } > free(blob); > blen = 0; > if ((r = sshkey_to_blob(match, &blob, &blen)) != 0) > error("%s: sshkey_to_blob: %s", __func__, > ssh_err(r)); > > //send sign request with the blob > if ((r = sshbuf_put_u8(msg, SSH2_AGENTC_SIGN_REQUEST)) != 0 || > (r = sshbuf_put_string(msg, blob, blen)) != 0 || > (r = sshbuf_put_string(msg, data, dlen)) != 0 || > (r = sshbuf_put_u32(msg, flags)) != 0) > fatal("%s: buffer error: %s", __func__, ssh_err(r)); > if ((r = ssh_request_reply(agent_fd, msg, msg)) != 0) > goto send; > > /* Get message type, and verify that we got a proper answer. */ > if ((r = sshbuf_get_u8(msg, &type)) != 0) > goto send; > if (type == SSH_AGENT_FAILURE || type == SSH2_AGENT_FAILURE) { > debug("agent failure"); > goto send; > } else if (type != SSH2_AGENT_SIGN_RESPONSE) { > debug("invalid format of response"); > goto send; > } > if ((r = sshbuf_get_string(msg, &signature, &slen)) != 0) > fatal("%s: buffer error: %s", __func__, ssh_err(r)); > /* Success */ > ok = 0; >send: > //end connection with ssh agent > ssh_close_authentication_socket(agent_fd); > ssh_free_identitylist(idlist); >send2: > sshkey_free(key); > if (ok == 0) { > if ((r = sshbuf_put_u8(reply, SSH2_AGENT_SIGN_RESPONSE)) != 0 || > (r = sshbuf_put_string(reply, signature, slen)) != 0) > fatal("%s: buffer error: %s", __func__, ssh_err(r)); > } else if ((r = sshbuf_put_u8(reply, SSH_AGENT_FAILURE)) != 0) > fatal("%s: buffer error: %s", __func__, ssh_err(r)); > if ((r = sshbuf_put_stringb(e->output, reply)) != 0) > fatal("%s: buffer error: %s", __func__, ssh_err(r)); > sshbuf_free(msg); > sshbuf_free(reply); > free(data); > free(blob); > free(signature); >} > >/* shared */ >static void >process_remove_identity(SocketEntry *e, int version) >{ > error("Cannot remove keys from this agent."); > process_fail(e); >} > >static void >process_remove_all_identities(SocketEntry *e, int version) >{ > error("Cannot remove keys from this agent."); > process_fail(e); >} > >/* removes expired keys and returns number of seconds until the next expiry */ >static time_t >reaper(void) >{ > return 0; >} > >static void >process_add_identity(SocketEntry *e, int version) >{ > error("Cannot add keys to this agent."); > process_fail(e); >} > >static void >process_lock_agent(SocketEntry *e, int lock) >{ > int r, success = 0, delay; > char *passwd, passwdhash[LOCK_SIZE]; > static u_int fail_count = 0; > size_t pwlen; > > if ((r = sshbuf_get_cstring(e->request, &passwd, &pwlen)) != 0) > fatal("%s: buffer error: %s", __func__, ssh_err(r)); > if (pwlen == 0) { > debug("empty password not supported"); > } else if (locked && !lock) { > if (bcrypt_pbkdf(passwd, pwlen, lock_salt, sizeof(lock_salt), > passwdhash, sizeof(passwdhash), LOCK_ROUNDS) < 0) > fatal("bcrypt_pbkdf"); > if (timingsafe_bcmp(passwdhash, lock_passwd, LOCK_SIZE) == 0) { > debug("agent unlocked"); > locked = 0; > fail_count = 0; > explicit_bzero(lock_passwd, sizeof(lock_passwd)); > success = 1; > } else { > /* delay in 0.1s increments up to 10s */ > if (fail_count < 100) > fail_count++; > delay = 100000 * fail_count; > debug("unlock failed, delaying %0.1lf seconds", > (double)delay/1000000); > usleep(delay); > } > explicit_bzero(passwdhash, sizeof(passwdhash)); > } else if (!locked && lock) { > debug("agent locked"); > locked = 1; > arc4random_buf(lock_salt, sizeof(lock_salt)); > if (bcrypt_pbkdf(passwd, pwlen, lock_salt, sizeof(lock_salt), > lock_passwd, sizeof(lock_passwd), LOCK_ROUNDS) < 0) > fatal("bcrypt_pbkdf"); > success = 1; > } > explicit_bzero(passwd, pwlen); > free(passwd); > send_status(e, success); >} > >static void >no_identities(SocketEntry *e, u_int type) >{ > struct sshbuf *msg; > int r; > > if ((msg = sshbuf_new()) == NULL) > fatal("%s: sshbuf_new failed", __func__); > if ((r = sshbuf_put_u8(msg, > (type == SSH_AGENTC_REQUEST_RSA_IDENTITIES) ? > SSH_AGENT_RSA_IDENTITIES_ANSWER : > SSH2_AGENT_IDENTITIES_ANSWER)) != 0 || > (r = sshbuf_put_u32(msg, 0)) != 0 || > (r = sshbuf_put_stringb(e->output, msg)) != 0) > fatal("%s: buffer error: %s", __func__, ssh_err(r)); > sshbuf_free(msg); >} > >#ifdef ENABLE_PKCS11 >static void >process_add_smartcard_key(SocketEntry *e) >{ > error("Cannot add keys to this agent."); > process_fail(e); >} > >static void >process_remove_smartcard_key(SocketEntry *e) >{ > error("Cannot remove keys from this agent."); > process_fail(e); >} >#endif /* ENABLE_PKCS11 */ > >/* dispatch incoming messages */ > >static void >process_message(SocketEntry *e) >{ > u_int msg_len; > u_char type; > const u_char *cp; > int r; > > if (sshbuf_len(e->input) < 5) > return; /* Incomplete message. */ > cp = sshbuf_ptr(e->input); > msg_len = PEEK_U32(cp); > if (msg_len > 256 * 1024) { > close_socket(e); > return; > } > if (sshbuf_len(e->input) < msg_len + 4) > return; > > /* move the current input to e->request */ > sshbuf_reset(e->request); > if ((r = sshbuf_get_stringb(e->input, e->request)) != 0 || > (r = sshbuf_get_u8(e->request, &type)) != 0) > fatal("%s: buffer error: %s", __func__, ssh_err(r)); > > /* check whether agent is locked */ > if (locked && type != SSH_AGENTC_UNLOCK) { > sshbuf_reset(e->request); > switch (type) { > case SSH_AGENTC_REQUEST_RSA_IDENTITIES: > case SSH2_AGENTC_REQUEST_IDENTITIES: > /* send empty lists */ > no_identities(e, type); > break; > default: > /* send a fail message for all other request types */ > send_status(e, 0); > } > return; > } > > debug("Received message type %d", type); > switch (type) { > case SSH_AGENTC_LOCK: > case SSH_AGENTC_UNLOCK: > process_lock_agent(e, type == SSH_AGENTC_LOCK); > break; >#ifdef WITH_SSH1 > /* ssh1 */ > case SSH_AGENTC_RSA_CHALLENGE: > process_authentication_challenge1(e); > break; > case SSH_AGENTC_REQUEST_RSA_IDENTITIES: > process_request_identities(e, 1); > break; > case SSH_AGENTC_ADD_RSA_IDENTITY: > case SSH_AGENTC_ADD_RSA_ID_CONSTRAINED: > process_add_identity(e, 1); > break; > case SSH_AGENTC_REMOVE_RSA_IDENTITY: > process_remove_identity(e, 1); > break; >#endif > case SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES: > process_remove_all_identities(e, 1); /* safe for !WITH_SSH1 */ > break; > /* ssh2 */ > case SSH2_AGENTC_SIGN_REQUEST: > process_sign_request_v2(e); > break; > case SSH2_AGENTC_REQUEST_IDENTITIES: > process_request_identities(e, 2); > break; > case SSH2_AGENTC_ADD_IDENTITY: > case SSH2_AGENTC_ADD_ID_CONSTRAINED: > process_add_identity(e, 2); > break; > case SSH2_AGENTC_REMOVE_IDENTITY: > process_remove_identity(e, 2); > break; > case SSH2_AGENTC_REMOVE_ALL_IDENTITIES: > process_remove_all_identities(e, 2); > break; >#ifdef ENABLE_PKCS11 > case SSH_AGENTC_ADD_SMARTCARD_KEY: > case SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED: > process_add_smartcard_key(e); > break; > case SSH_AGENTC_REMOVE_SMARTCARD_KEY: > process_remove_smartcard_key(e); > break; >#endif /* ENABLE_PKCS11 */ > default: > /* Unknown message. Respond with failure. */ > error("Unknown message %d", type); > sshbuf_reset(e->request); > send_status(e, 0); > break; > } >} > >static void >new_socket(sock_type type, int fd) >{ > u_int i, old_alloc, new_alloc; > > set_nonblock(fd); > > if (fd > max_fd) > max_fd = fd; > > for (i = 0; i < sockets_alloc; i++) > if (sockets[i].type == AUTH_UNUSED) { > sockets[i].fd = fd; > if ((sockets[i].input = sshbuf_new()) == NULL) > fatal("%s: sshbuf_new failed", __func__); > if ((sockets[i].output = sshbuf_new()) == NULL) > fatal("%s: sshbuf_new failed", __func__); > if ((sockets[i].request = sshbuf_new()) == NULL) > fatal("%s: sshbuf_new failed", __func__); > sockets[i].type = type; > return; > } > old_alloc = sockets_alloc; > new_alloc = sockets_alloc + 10; > sockets = xreallocarray(sockets, new_alloc, sizeof(sockets[0])); > for (i = old_alloc; i < new_alloc; i++) > sockets[i].type = AUTH_UNUSED; > sockets_alloc = new_alloc; > sockets[old_alloc].fd = fd; > if ((sockets[old_alloc].input = sshbuf_new()) == NULL) > fatal("%s: sshbuf_new failed", __func__); > if ((sockets[old_alloc].output = sshbuf_new()) == NULL) > fatal("%s: sshbuf_new failed", __func__); > if ((sockets[old_alloc].request = sshbuf_new()) == NULL) > fatal("%s: sshbuf_new failed", __func__); > sockets[old_alloc].type = type; >} > >static int >prepare_select(fd_set **fdrp, fd_set **fdwp, int *fdl, u_int *nallocp, > struct timeval **tvpp) >{ > u_int i, sz; > int n = 0; > static struct timeval tv; > time_t deadline; > > for (i = 0; i < sockets_alloc; i++) { > switch (sockets[i].type) { > case AUTH_SOCKET: > case AUTH_CONNECTION: > n = MAX(n, sockets[i].fd); > break; > case AUTH_UNUSED: > break; > default: > fatal("Unknown socket type %d", sockets[i].type); > break; > } > } > > sz = howmany(n+1, NFDBITS) * sizeof(fd_mask); > if (*fdrp == NULL || sz > *nallocp) { > free(*fdrp); > free(*fdwp); > *fdrp = xmalloc(sz); > *fdwp = xmalloc(sz); > *nallocp = sz; > } > if (n < *fdl) > debug("XXX shrink: %d < %d", n, *fdl); > *fdl = n; > memset(*fdrp, 0, sz); > memset(*fdwp, 0, sz); > > for (i = 0; i < sockets_alloc; i++) { > switch (sockets[i].type) { > case AUTH_SOCKET: > case AUTH_CONNECTION: > FD_SET(sockets[i].fd, *fdrp); > if (sshbuf_len(sockets[i].output) > 0) > FD_SET(sockets[i].fd, *fdwp); > break; > default: > break; > } > } > deadline = reaper(); > if (parent_alive_interval != 0) > deadline = (deadline == 0) ? parent_alive_interval : > MIN(deadline, parent_alive_interval); > if (deadline == 0) { > *tvpp = NULL; > } else { > tv.tv_sec = deadline; > tv.tv_usec = 0; > *tvpp = &tv; > } > return (1); >} > >static void >after_select(fd_set *readset, fd_set *writeset) >{ > struct sockaddr_un sunaddr; > socklen_t slen; > char buf[1024]; > int len, sock, r; > u_int i, orig_alloc; > uid_t euid; > gid_t egid; > > for (i = 0, orig_alloc = sockets_alloc; i < orig_alloc; i++) > switch (sockets[i].type) { > case AUTH_UNUSED: > break; > case AUTH_SOCKET: > if (FD_ISSET(sockets[i].fd, readset)) { > slen = sizeof(sunaddr); > sock = accept(sockets[i].fd, > (struct sockaddr *)&sunaddr, &slen); > if (sock < 0) { > error("accept from AUTH_SOCKET: %s", > strerror(errno)); > break; > } > if (getpeereid(sock, &euid, &egid) < 0) { > error("getpeereid %d failed: %s", > sock, strerror(errno)); > close(sock); > break; > } > if ((euid != 0) && (getuid() != euid)) { > error("uid mismatch: " > "peer euid %u != uid %u", > (u_int) euid, (u_int) getuid()); > close(sock); > break; > } > new_socket(AUTH_CONNECTION, sock); > } > break; > case AUTH_CONNECTION: > if (sshbuf_len(sockets[i].output) > 0 && > FD_ISSET(sockets[i].fd, writeset)) { > len = write(sockets[i].fd, > sshbuf_ptr(sockets[i].output), > sshbuf_len(sockets[i].output)); > if (len == -1 && (errno == EAGAIN || > errno == EWOULDBLOCK || > errno == EINTR)) > continue; > if (len <= 0) { > close_socket(&sockets[i]); > break; > } > if ((r = sshbuf_consume(sockets[i].output, > len)) != 0) > fatal("%s: buffer error: %s", > __func__, ssh_err(r)); > } > if (FD_ISSET(sockets[i].fd, readset)) { > len = read(sockets[i].fd, buf, sizeof(buf)); > if (len == -1 && (errno == EAGAIN || > errno == EWOULDBLOCK || > errno == EINTR)) > continue; > if (len <= 0) { > close_socket(&sockets[i]); > break; > } > if ((r = sshbuf_put(sockets[i].input, > buf, len)) != 0) > fatal("%s: buffer error: %s", > __func__, ssh_err(r)); > explicit_bzero(buf, sizeof(buf)); > process_message(&sockets[i]); > } > break; > default: > fatal("Unknown type %d", sockets[i].type); > } >} > >static void >cleanup_socket(void) >{ > if (cleanup_pid != 0 && getpid() != cleanup_pid) > return; > debug("%s: cleanup", __func__); > if (socket_name[0]) > unlink(socket_name); > if (socket_dir[0]) > rmdir(socket_dir); >} > >void >cleanup_exit(int i) >{ > cleanup_socket(); > _exit(i); >} > >/*ARGSUSED*/ >static void >cleanup_handler(int sig) >{ > cleanup_socket(); >#ifdef ENABLE_PKCS11 > pkcs11_terminate(); >#endif > _exit(2); >} > >static void >check_parent_exists(void) >{ > /* > * If our parent has exited then getppid() will return (pid_t)1, > * so testing for that should be safe. > */ > if (parent_pid != -1 && getppid() != parent_pid) { > /* printf("Parent has died - Authentication agent exiting.\n"); */ > cleanup_socket(); > _exit(2); > } >} > >static void >usage(void) >{ > fprintf(stderr, > "usage: ssh-certificate-agent -f certificate_file [-c | -s] [-Dd] [-a bind_address] \n" > " [-E fingerprint_hash] [-t life] [command [arg ...]]\n" > " ssh-certificate-agent [-c | -s] -k\n"); > exit(1); >} > >int >main(int ac, char **av) >{ > int c_flag = 0, d_flag = 0, D_flag = 0, k_flag = 0, s_flag = 0, f_flag = 0; > int sock, fd, ch, result, saved_errno; > u_int nalloc; > char *shell, *format, *pidstr, *agentsocket = NULL; > fd_set *readsetp = NULL, *writesetp = NULL; >#ifdef HAVE_SETRLIMIT > struct rlimit rlim; >#endif > extern int optind; > extern char *optarg; > pid_t pid; > char pidstrbuf[1 + 3 * sizeof pid]; > struct timeval *tvp = NULL; > size_t len; > mode_t prev_mask; > > /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ > sanitise_stdfd(); > > /* drop */ > setegid(getgid()); > setgid(getgid()); > >#if defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE) > /* Disable ptrace on Linux without sgid bit */ > prctl(PR_SET_DUMPABLE, 0); >#endif > >#ifdef WITH_OPENSSL > OpenSSL_add_all_algorithms(); >#endif > > __progname = ssh_get_progname(av[0]); > seed_rng(); > > while ((ch = getopt(ac, av, "cDdksE:a:t:f:")) != -1) { > switch (ch) { > case 'f': > add_agent_certificate(optarg); > f_flag++; > break; > case 'E': > fingerprint_hash = ssh_digest_alg_by_name(optarg); > if (fingerprint_hash == -1) > fatal("Invalid hash algorithm \"%s\"", optarg); > break; > case 'c': > if (s_flag) > usage(); > c_flag++; > break; > case 'k': > k_flag++; > break; > case 's': > if (c_flag) > usage(); > s_flag++; > break; > case 'd': > if (d_flag || D_flag) > usage(); > d_flag++; > break; > case 'D': > if (d_flag || D_flag) > usage(); > D_flag++; > break; > case 'a': > agentsocket = optarg; > break; > case 't': > if ((lifetime = convtime(optarg)) == -1) { > fprintf(stderr, "Invalid lifetime\n"); > usage(); > } > break; > default: > usage(); > } > } > ac -= optind; > av += optind; > if (!k_flag && !f_flag) > usage(); > if (ac > 0 && (c_flag || k_flag || s_flag || D_flag || d_flag)) > usage(); > if (ac == 0 && !c_flag && !s_flag) { > shell = getenv("SHELL"); > if (shell != NULL && (len = strlen(shell)) > 2 && > strncmp(shell + len - 3, "csh", 3) == 0) > c_flag = 1; > } > if (k_flag) { > const char *errstr = NULL; > > pidstr = getenv(SSH_CERTAGENTPID_ENV_NAME); > if (pidstr == NULL) { > fprintf(stderr, "%s not set, cannot kill agent\n", > SSH_CERTAGENTPID_ENV_NAME); > exit(1); > } > pid = (int)strtonum(pidstr, 2, INT_MAX, &errstr); > if (errstr) { > fprintf(stderr, > "%s=\"%s\", which is not a good PID: %s\n", > SSH_CERTAGENTPID_ENV_NAME, pidstr, errstr); > exit(1); > } > if (kill(pid, SIGTERM) == -1) { > perror("kill"); > exit(1); > } > format = c_flag ? "unsetenv %s;\n" : "unset %s;\n"; > printf(format, SSH_CERTSOCKET_ENV_NAME); > printf(format, SSH_CERTAGENTPID_ENV_NAME); > printf("echo Agent pid %ld killed;\n", (long)pid); > exit(0); > } > //finish loading cert > parent_pid = getpid(); > > if (agentsocket == NULL) { > /* Create private directory for agent socket */ > mktemp_proto(socket_dir, sizeof(socket_dir)); > if (mkdtemp(socket_dir) == NULL) { > perror("mkdtemp: private socket dir"); > exit(1); > } > snprintf(socket_name, sizeof socket_name, "%s/agent.%ld", socket_dir, > (long)parent_pid); > } else { > /* Try to use specified agent socket */ > socket_dir[0] = '\0'; > strlcpy(socket_name, agentsocket, sizeof socket_name); > } > > /* > * Create socket early so it will exist before command gets run from > * the parent. > */ > prev_mask = umask(0177); > sock = unix_listener(socket_name, SSH_LISTEN_BACKLOG, 0); > if (sock < 0) { > /* XXX - unix_listener() calls error() not perror() */ > *socket_name = '\0'; /* Don't unlink any existing file */ > cleanup_exit(1); > } > umask(prev_mask); > > /* > * Fork, and have the parent execute the command, if any, or present > * the socket data. The child continues as the authentication agent. > */ > if (D_flag || d_flag) { > log_init(__progname, > d_flag ? SYSLOG_LEVEL_DEBUG3 : SYSLOG_LEVEL_INFO, > SYSLOG_FACILITY_AUTH, 1); > format = c_flag ? "setenv %s %s;\n" : "%s=%s; export %s;\n"; > printf(format, SSH_CERTSOCKET_ENV_NAME, socket_name, > SSH_CERTSOCKET_ENV_NAME); > printf("echo Agent pid %ld;\n", (long)parent_pid); > goto skip; > } > > pid = fork(); > if (pid == -1) { > perror("fork"); > cleanup_exit(1); > } > if (pid != 0) { /* Parent - execute the given command. */ > close(sock); > snprintf(pidstrbuf, sizeof pidstrbuf, "%ld", (long)pid); > if (ac == 0) { > format = c_flag ? "setenv %s %s;\n" : "%s=%s; export %s;\n"; > printf(format, SSH_CERTSOCKET_ENV_NAME, socket_name, > SSH_CERTSOCKET_ENV_NAME); > printf(format, SSH_CERTAGENTPID_ENV_NAME, pidstrbuf, > SSH_CERTAGENTPID_ENV_NAME); > printf("echo Agent pid %ld;\n", (long)pid); > exit(0); > } > if (setenv(SSH_CERTSOCKET_ENV_NAME, socket_name, 1) == -1 || > setenv(SSH_CERTAGENTPID_ENV_NAME, pidstrbuf, 1) == -1) { > perror("setenv"); > exit(1); > } > execvp(av[0], av); > perror(av[0]); > exit(1); > } > /* child */ > log_init(__progname, SYSLOG_LEVEL_INFO, SYSLOG_FACILITY_AUTH, 0); > > if (setsid() == -1) { > error("setsid: %s", strerror(errno)); > cleanup_exit(1); > } > > (void)chdir("/"); > if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) { > /* XXX might close listen socket */ > (void)dup2(fd, STDIN_FILENO); > (void)dup2(fd, STDOUT_FILENO); > (void)dup2(fd, STDERR_FILENO); > if (fd > 2) > close(fd); > } > >#ifdef HAVE_SETRLIMIT > /* deny core dumps, since memory contains unencrypted private keys */ > rlim.rlim_cur = rlim.rlim_max = 0; > if (setrlimit(RLIMIT_CORE, &rlim) < 0) { > error("setrlimit RLIMIT_CORE: %s", strerror(errno)); > cleanup_exit(1); > } >#endif > >skip: > > cleanup_pid = getpid(); > >#ifdef ENABLE_PKCS11 > pkcs11_init(0); >#endif > new_socket(AUTH_SOCKET, sock); > if (ac > 0) > parent_alive_interval = 10; > idtab_init(); > load_agent_certificates(); > signal(SIGPIPE, SIG_IGN); > signal(SIGINT, (d_flag | D_flag) ? cleanup_handler : SIG_IGN); > signal(SIGHUP, cleanup_handler); > signal(SIGTERM, cleanup_handler); > nalloc = 0; > while (1) { > prepare_select(&readsetp, &writesetp, &max_fd, &nalloc, &tvp); > result = select(max_fd + 1, readsetp, writesetp, NULL, tvp); > saved_errno = errno; > if (parent_alive_interval != 0) > check_parent_exists(); > (void) reaper(); /* remove expired keys */ > if (result < 0) { > if (saved_errno == EINTR) > continue; > fatal("select: %s", strerror(saved_errno)); > } else if (result > 0) > after_select(readsetp, writesetp); > } > /* NOTREACHED */ >}
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Raw
Actions:
View
Attachments on
bug 2436
:
2679
| 2694 |
2700