ssh-agent.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
* All rights reserved
* The authentication agent program.
*
* As far as I am concerned, the code I have written for this software
* can be used freely for any purpose. Any derived versions of this
* software must be clearly marked as such, and if the derived work is
* incompatible with the protocol description in the RFC file, it must be
* called by a name other than "ssh" or "Secure Shell".
*
* Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "includes.h"
#include "sys-queue.h"
RCSID("$OpenBSD: ssh-agent.c,v 1.105 2002/10/01 20:34:12 markus Exp $");
#pragma ident "%Z%%M% %I% %E% SMI"
#ifdef HAVE_SOLARIS_PRIVILEGE
#include <priv.h>
#endif /* HAVE_SOLARIS_PRIVILEGE */
#include "ssh.h"
#include "rsa.h"
#include "buffer.h"
#include "bufaux.h"
#include "xmalloc.h"
#include "getput.h"
#include "key.h"
#include "authfd.h"
#include "compat.h"
#include "log.h"
#ifdef SMARTCARD
#include "scard.h"
#endif
typedef enum {
} sock_type;
typedef struct {
int fd;
} SocketEntry;
u_int sockets_alloc = 0;
typedef struct identity {
char *comment;
} Identity;
typedef struct {
int nentries;
} Idtab;
/* private key table, one per protocol version */
int max_fd = 0;
/* pid of shell == parent of agent */
/* pathname and directory for AUTH_SOCKET */
char socket_name[1024];
char socket_dir[1024];
/* locking */
int locked = 0;
char *lock_passwd = NULL;
#ifdef HAVE___PROGNAME
extern char *__progname;
#else
char *__progname;
#endif
static void
{
e->fd = -1;
e->type = AUTH_UNUSED;
buffer_free(&e->input);
buffer_free(&e->output);
buffer_free(&e->request);
}
static void
idtab_init(void)
{
int i;
for (i = 0; i <=2; i++) {
}
}
/* return private key table for requested protocol version */
static Idtab *
idtab_lookup(int version)
{
}
static void
{
}
/* return matching private key for given public key */
static Identity *
{
return (id);
}
return (NULL);
}
/* send list of supported public keys to 'client' */
static void
{
buffer_init(&msg);
} else {
}
}
buffer_free(&msg);
}
/* ssh1 only */
static void
{
int i, len;
buffer_init(&msg);
fatal("process_authentication_challenge1: BN_new failed");
/* Only protocol 1.1 is supported */
if (buffer_len(&e->request) == 0)
goto failure;
if (response_type != 1)
goto failure;
/* Decrypt the challenge using the private key. */
goto failure;
/* The response is MD5 of decrypted challenge plus session id. */
goto failure;
}
/* Send the response. */
for (i = 0; i < 16; i++)
goto send;
}
/* Unknown identity or protocol error. Send failure. */
send:
buffer_free(&msg);
}
/* ssh2 only */
static void
{
extern int datafellows;
datafellows = 0;
if (flags & SSH_AGENT_OLD_SIGNATURE)
}
buffer_init(&msg);
if (ok == 0) {
} else {
}
buffer_len(&msg));
buffer_free(&msg);
}
/* shared */
static void
{
int success = 0;
switch (version) {
case 1:
log("Warning: identity keysize mismatch: actual %u, announced %u",
break;
case 2:
break;
}
/*
* We have this key. Free the old key. Since we
* don\'t want to leave empty slots in the middle of
* the array, we actually free the key there and move
* all the entries between the empty slot and the end
* of the array.
*/
fatal("process_remove_identity: "
"internal error: tab->nentries %d",
success = 1;
}
}
buffer_put_char(&e->output,
}
static void
{
/* Loop over all identities and clear the keys. */
}
/* Mark that there are no identities. */
/* Send success. */
}
static void
reaper(void)
{
int version;
}
}
}
}
static void
{
switch (version) {
case 1:
k = key_new_private(KEY_RSA1);
/* SSH and SSL have p and q swapped */
/* Generate additional parameters */
break;
case 2:
switch (type) {
case KEY_DSA:
k = key_new_private(type);
break;
case KEY_RSA:
k = key_new_private(type);
/* Generate additional parameters */
break;
default:
buffer_clear(&e->request);
goto send;
}
break;
}
if (k == NULL) {
goto send;
}
success = 1;
while (buffer_len(&e->request)) {
switch (buffer_get_char(&e->request)) {
break;
default:
break;
}
}
/* Increment the number of identities. */
} else {
key_free(k);
}
send:
buffer_put_char(&e->output,
}
/* XXX todo: encrypt sensitive data with passphrase */
static void
{
int success = 0;
char *passwd;
locked = 0;
lock_passwd = NULL;
success = 1;
locked = 1;
success = 1;
}
buffer_put_char(&e->output,
}
static void
{
buffer_init(&msg);
buffer_put_int(&msg, 0);
buffer_free(&msg);
}
#ifdef SMARTCARD
static void
{
error("sc_get_keys failed");
goto send;
}
k = keys[i];
success = 1;
} else {
key_free(k);
}
}
send:
buffer_put_char(&e->output,
}
static void
{
error("sc_get_keys failed");
goto send;
}
k = keys[i];
success = 1;
}
key_free(k);
}
send:
buffer_put_char(&e->output,
}
#endif /* SMARTCARD */
/* dispatch incoming messages */
static void
{
/* kill dead keys */
reaper();
return; /* Incomplete message. */
close_socket(e);
return;
}
return;
/* move the current input to e->request */
buffer_clear(&e->request);
/* check wheter agent is locked */
buffer_clear(&e->request);
switch (type) {
/* send empty lists */
no_identities(e, type);
break;
default:
/* send a fail message for all other request types */
}
return;
}
switch (type) {
case SSH_AGENTC_LOCK:
case SSH_AGENTC_UNLOCK:
break;
/* ssh1 */
case SSH_AGENTC_RSA_CHALLENGE:
break;
process_request_identities(e, 1);
break;
process_add_identity(e, 1);
break;
process_remove_identity(e, 1);
break;
break;
/* ssh2 */
case SSH2_AGENTC_SIGN_REQUEST:
break;
process_request_identities(e, 2);
break;
case SSH2_AGENTC_ADD_IDENTITY:
process_add_identity(e, 2);
break;
process_remove_identity(e, 2);
break;
break;
#ifdef SMARTCARD
break;
break;
#endif /* SMARTCARD */
default:
/* Unknown message. Respond with failure. */
buffer_clear(&e->request);
break;
}
}
static void
{
for (i = 0; i < sockets_alloc; i++)
return;
}
sockets_alloc += 10;
if (sockets)
else
for (i = old_alloc; i < sockets_alloc; i++)
}
static int
{
int n = 0;
for (i = 0; i < sockets_alloc; i++) {
case AUTH_SOCKET:
case AUTH_CONNECTION:
break;
case AUTH_UNUSED:
break;
default:
break;
}
}
if (*fdrp)
if (*fdwp)
}
if (n < *fdl)
*fdl = n;
for (i = 0; i < sockets_alloc; i++) {
case AUTH_SOCKET:
case AUTH_CONNECTION:
break;
default:
break;
}
}
return (1);
}
static void
{
struct sockaddr_un sunaddr;
char buf[1024];
u_int i;
for (i = 0; i < sockets_alloc; i++)
case AUTH_UNUSED:
break;
case AUTH_SOCKET:
if (sock < 0) {
error("accept from AUTH_SOCKET: %s",
break;
}
error("getpeereid %d failed: %s",
break;
}
error("uid mismatch: "
"peer euid %u != uid %u",
break;
}
}
break;
case AUTH_CONNECTION:
do {
continue;
break;
} while (1);
if (len <= 0) {
close_socket(&sockets[i]);
break;
}
}
do {
continue;
break;
} while (1);
if (len <= 0) {
close_socket(&sockets[i]);
break;
}
process_message(&sockets[i]);
}
break;
default:
}
}
static void
cleanup_socket(void *p)
{
if (socket_name[0])
if (socket_dir[0])
}
static void
cleanup_exit(int i)
{
exit(i);
}
static void
cleanup_handler(int sig)
{
_exit(2);
}
static void
check_parent_exists(int sig)
{
int save_errno = errno;
/* printf("Parent has died - Authentication agent exiting.\n"); */
}
alarm(10);
errno = save_errno;
}
static void
usage(void)
{
gettext("Usage: %s [options] [command [args ...]]\n"
"Options:\n"
" -c Generate C-shell commands on stdout.\n"
" -s Generate Bourne shell commands on stdout.\n"
" -k Kill the current agent.\n"
" -d Debug mode.\n"
" -a socket Bind agent socket to given name.\n"),
exit(1);
}
int
{
const char *format;
struct sockaddr_un sunaddr;
#ifdef HAVE_SETRLIMIT
#endif
#ifdef HAVE_CYGWIN
int prev_mask;
#endif
extern int optind;
extern char *optarg;
#ifdef HAVE_SOLARIS_PRIVILEGE
#endif /* HAVE_SOLARIS_PRIVILEGE */
/* drop */
init_rng();
seed_rng();
switch (ch) {
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)
usage();
d_flag++;
break;
case 'a':
break;
default:
usage();
}
}
usage();
c_flag = 1;
}
if (k_flag) {
gettext("%s not set, cannot kill agent\n"),
exit(1);
}
if (pid < 1) {
gettext("%s=\")%s\", which is not a good PID\n"),
exit(1);
}
perror("kill");
exit(1);
}
printf("echo ");
exit(0);
}
parent_pid = getpid();
if (agentsocket == NULL) {
/* Create private directory for agent socket */
perror("mkdtemp: private socket dir");
exit(1);
}
(long)parent_pid);
} else {
/* Try to use specified agent socket */
socket_dir[0] = '\0';
}
/*
* Create socket early so it will exist before command gets run from
* the parent.
*/
if (sock < 0) {
perror("socket");
cleanup_exit(1);
}
#ifdef HAVE_CYGWIN
#endif
perror("bind");
#ifdef HAVE_CYGWIN
#endif
cleanup_exit(1);
}
#ifdef HAVE_CYGWIN
#endif
perror("listen");
cleanup_exit(1);
}
/*
* 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) {
printf("echo ");
goto skip;
}
if (pid == -1) {
perror("fork");
cleanup_exit(1);
}
if (pid != 0) { /* Parent - execute the given command. */
if (ac == 0) {
printf("echo ");
exit(0);
}
perror("setenv");
exit(1);
}
exit(1);
}
/* child */
#ifdef HAVE_SOLARIS_PRIVILEGE
/*
*
* Idiom: remove from 'basic' privs we know we don't want,
* invert the result and remove the resulting set from P.
*
* None of the priv_delset() calls below, nor the setppriv call
* below can fail, so their return values are not checked.
*/
fatal("priv_str_to_set failed: %m");
(void) priv_freeset(myprivs);
#endif /* HAVE_SOLARIS_PRIVILEGE */
if (setsid() == -1) {
cleanup_exit(1);
}
(void)chdir("/");
close(0);
close(1);
close(2);
#ifdef HAVE_SETRLIMIT
/* deny core dumps, since memory contains unencrypted private keys */
cleanup_exit(1);
}
#endif
skip:
if (ac > 0) {
alarm(10);
}
idtab_init();
if (!d_flag)
nalloc = 0;
while (1) {
continue;
}
}
/* NOTREACHED */
return (0); /* keep lint happy */
}