Commit a5c91a7d authored by Niels Möller's avatar Niels Möller

New files.

Rev: src/charset.c:1.1
Rev: src/charset.h:1.1
Rev: src/client_password.c:1.1
Rev: src/client_userauth.c:1.1
Rev: src/client_userauth.h:1.1
Rev: src/password.h:1.1
Rev: src/server_password.c:1.1
parent dba58dee
/* charset.c
*
* Translate local characterset to and from utf8.
*
* $Id$
*/
/* lsh, an implementation of the ssh protocol
*
* Copyright (C) 1998 Niels Mller
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "charset.h"
#include <assert.h>
static int local_charset;
void set_local_charset(int charset)
{
local_charset = charset;
}
UINT32 local_to_ucs4(int c)
{
switch (local_charset)
{
case CHARSET_US_ASCII:
case CHARSET_LATIN1:
return (UINT32) c;
default:
fatal("Internal error");
};
}
/* NOTE: This function does not filter any control characters */
int ucs4_to_local(UINT32 c)
{
switch (local_charset)
{
case CHARSET_US_ASCII:
return (c < 0x80) ? c : -1;
case CHARSET_LATIN1:
return (c < 0x100) ? c : -1;
default:
fatal("Internal error");
};
}
struct lsh_string *local_to_utf8(struct lsh_string s, int free)
{
switch (local_charset)
{
case CHARSET_UTF8:
case CHARSET_USASCII:
return s;
default:
{
UINT32 *chars = alloca(s->length * sizeof(UINT32));
unsigned char *lengths = alloca(s->length);
UINT32 total = 0;
{
int i;
/* First convert to ucs-4, and compute the length of the corresponding
* utf-8 string. */
for (i = 0; i<s->length; i++)
{
UINT32 c = local_to_ucs4(s->data[i]);
unsigned char l = 1;
if (c >= (1L<<7))
{
l++;
if (c >= (1L<<11))
{
l++;
if (c >= (1L<<16))
{
l++;
if (c >= (1L<<21))
{
l++;
if (c >= (1L<<25))
{
l++;
if (c >= (1L<<31))
fatal("Internal error!\n");
}}}}}
chars[i] = c;
lengths[i] = l;
total += l;
}
}
{
struct lsh_string res = lsh_string_alloc(total);
int i, j;
for(i = j = 0; i<s->length; i++)
{
static const UINT8 *prefix
= {0, 0xC0, 0xE0, 0xF0, 0xF8, 0xFc };
UINT32 c = chars[i];
unsigned char l = lengths[i] - 1;
int k;
for (k = l; k; k--)
{
res->data[j+k] = 0x80 | (c & 0x3f);
c >>= 6;
}
assert( !(prefix[l] & c) );
res->data[j] = prefix[l] | c;
j += lengths[i];
}
assert(j == total);
if (free)
lsh_string_free(s);
return res;
}
}
}
}
struct lsh_string *utf8_to_local(struct lsh_string s, int free)
{
int i;
struct lsh_string *res;
struct simple_buffer buffer;
if (local_charset == CHARSET_UTF8)
return s;
/* The string can't grow when converted to local charset */
res = lsh_string_alloc(s->length);
simple_buffer_init(&buffer, s->length, s->data);
for (i = 0; 1; i++)
{
UINT32 ucs4;
switch(parse_utf8(&buffer, &ucs4))
{
case -1:
assert(i<res->length);
res->length = i;
if (free)
lsh_string_free(s);
return res;
case 1:
{
int local = ucs4_to_local(ucs4);
if (local >= 0)
{
res->data[i] = local;
break;
}
/* Fall through */
}
case 0: /* Error */
lsh_string_free(res);
if (free)
lsh_string_free(s);
return 0;
default:
fatal("Internal error!\n");
}
}
}
/* charset.h
*
* Translate local characterset to and from utf8.
*
* $Id$
*/
/* lsh, an implementation of the ssh protocol
*
* Copyright (C) 1998 Niels Möller
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef LSH_CHARSET_H_INCLUDED
#define LSH_CHARSET_H_INCLUDED
#include "lsh_types.h"
/* FIXME: Use charsets real objects, instead of using fixed constants */
#define CHARSET_UTF8 0
#define CHARSET_LATIN1 1
#define CHARSET_USASCII 2
void set_local_charset(int charset);
UINT32 local_to_ucs4(int c);
int ucs4_to_local(UINT32 c);
struct lsh_string *local_to_utf8(struct lsh_string s, int free);
struct lsh_string *utf8_to_local(struct lsh_string s, int free);
#endif /* LSH_CHARSET_H_INCLUDED */
/* client_password.c
*
* System dependant password related functions.
*/
/* lsh, an implementation of the ssh protocol
*
* Copyright (C) 1998 Niels Mller
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "password.h"
#include <stdarg.h>
#include <string.h>
#include <termios.h>
int echo_on(int fd)
{
struct termios t;
if (tcgetattr(fd, &t) < 0)
{
werror("Can't get terminal attributes: %s\n", strerror(errno));
return 0;
}
t->c_lflag |= ECHO;
if (tcsetattr(fd, TCSANOW, &t) < 0)
{
werror("Can't set terminal attributes: %s\n", strerror(errno));
return 0;
}
return 1;
}
int echo_off(int fd)
{
struct termios t;
if (tcgetattr(fd, &t) < 0)
{
werror("Can't get terminal attributes: %s\n", strerror(errno));
return 0;
}
t->c_lflag &= ~ECHO;
if (tcsetattr(fd, TCSAFLUSH, &t) < 0)
{
werror("Can't set terminal attributes: %s\n", strerror(errno));
return 0;
}
return 1;
}
/* FIXME: Perhaps it is better to avoid using stdio functions? */
struct lsh_string *read_password(int max_length, char *format, ...)
{
va_list args;
int fd;
FILE *tty;
struct lsh_string *res;
fd = open("/dev/tty", O_RDWR);
if (fd < 0)
{
werror("Can't open /dev/tty: %s\n", strerror(errno));
return 0;
}
tty = fdopen(fd, "rw");
if (!tty)
{
close(fd);
werror("Can't fdopen /dev/tty: %s\n", strerror(errno));
return 0;
}
/* Ignore errors */
(void) echo_off(fd);
va_start(args, format);
vfprintf(tty, format, format, args);
va_end(args);
fflush(tty);
res = lsh_string_alloc(max_length);
if (!fgets(res->data, max_length, tty))
res = 0;
else
{
res->length = strlen(res->data);
/* Delete terminating newline */
if (res->length && (res->data[res->length-1] == '\n'))
res->length--;
}
/* Ignore errors */
(void) echo_on(fd);
fclose(tty);
return res;
}
/* client_userauth.c
*
*/
/* lsh, an implementation of the ssh protocol
*
* Copyright (C) 1998 Niels Mller
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "userauth.h"
#include "service.h"
/* FIXME: For now, use only password authentication. A better method
* would be to first send a set of publickey authentication requests
* for the available keys (for some configurable value of
* "available"). This is analogous to unlocking a door by first
* examining the keys on one's keyring to see if any of them kan be
* inserted into the lock. Preferably, at this point one should use
* spki hashed public keys rather than the public keys themselves.
*
* Next we wait for SSH_MSH_USERAUTH_FAILURE or SSH_MSG_USERAUTH_PK_OK
* messages. If any of the keys is recognized, we compute a signature
* and send it to the server (analogously to inserting the key into
* the lock and turning it around).
*
* If none of the keys were recognized, or if no keys were available
*from the start, we ask the user for a password and attempts to log
*in using that. */
struct client_userauth
{
struct ssh_service super;
struct lsh_string username; /* Remote user name to authenticate as */
int service_name; /* Service we want to access */
struct ssh_service *service;
/* FIXME: Keys to try */
};
struct success_handler
{
struct packet_handler super;
struct ssh_service *service;
};
struct failure_handler
{
struct packet_handler super;
struct client_userauth *userauth;
};
struct lsh_string format_userauth_password(struct lsh_string name,
int service,
struct lsh_string passwd
int free)
{
return ssh_format(free ? "%c%S%a%a%c%fS" : "%c%S%a%a%c%S",
SSH_MSG_USERAUTH_REQUEST,
name,
service,
ATOM_PASSWORD,
0,
passwd);
}
#define MAX_PASSWD 100
/* FIXME: Implement proper conversions from local charset */
#define LOCAL_TO_UTF8(x, free) (x)
static int send_passwd(struct client_userauth *userauth,
struct ssh_connection *connection)
{
struct lsh_string *passwd
= read_password(MAX_PASSWD, "Password for %s: ", userauth->name);
if (!passwd)
return LSH_FAIL | LSH_DIE;
return A_WRITE(connection->write,
format_userauth_password(LOCAL_TO_UTF8(userauth->name, 0),
service,
LOCAL_TO_UTF8(passwd, 1),
1));
}
static int do_userauth_success(struct packet_handler *c,
struct ssh_connection *connection,
struct lsh_string *packet)
{
struct success_handler *closure = (struct success_handler *) c;
struct simple_buffer buffer;
int msg_number;
MDEBUG(closure);
simple_buffer_init(&buffer, packet->length, packet->data);
if (parse_uint8(&buffer, &msg_number)
&& (msg_number == SSH_MSG_USERAUTH_SUCCESS)
&& parse_eod(&buffer))
{
werror("User authentication successful.\n");
lsh_string_free(packet);
connection->dispatch[SSH_MSG_USERAUTH_SUCCESS] = connection->fail;
connection->dispatch[SSH_MSG_USERAUTH_FAILURE] = connection->fail;
connection->dispatch[SSH_MSG_USERAUTH_BANNER] = connection->fail;
return SERVICE_INIT(closure->service, connection);
}
lsh_string_free(packet);
return LSH_FAIL | LSSH_DIE;
}
static int do_userauth_failure(struct packet_handler *c,
struct ssh_connection *connection,
struct lsh_string *packet)
{
struct failure_handler *closure = (struct failure_handler *) c;
struct simple_buffer buffer;
int msg_number;
int *methods = NULL;
int partial_success:
MDEBUG(closure);
simple_buffer_init(&buffer, packet->length, packet->data);
if (parse_uint8(&buffer, &msg_number)
&& (msg_number == SSH_MSG_USERAUTH_FAILURE)
&& parse_atoms(&buffer, &methods)
&& parse_boolean(&buffer, &methods)
&& parse_eod(&buffer))
{
int i;
lsh_string_free(packet);
if (partial_success)
{ /* Doesn't help us */
werror("Recieved SSH_MSH_USERAUTH_FAILURE "
"indicating partial success.\n")
lsh_free(methods);
return LSH_FAIL | LSH_DIE;
}
for(i = 0; methods[i] >= 0; i++)
if (methods[i] == ATOM_PASSWORD)
{
/* Try again */
lsh_free(methods);
return send_passwd(closure->userauth, connection);
}
/* No methods that we can use */
lsh_free(methods);
return LSH_FAIL | LSH_DIE;
}
if (methods)
lsh_free(methods);
lsh_string_free(packet);
return LSH_FAIL | LSSH_DIE;
}
static int do_userauth_banner(struct packet_handler *closure
struct ssh_connection *connection,
struct lsh_string *packet)
{
struct simple_buffer buffer;
int msg_number;
UINT32 length;
UINT8 *mesg;
UINT32 language_length;
UINT8 *language;
MDEBUG(closure);
simple_buffer_init(&buffer, packet->length, packet->data);
if (parse_uint8(&buffer, &msg_number)
&& (msg_number == SSH_MSG_USERAUTH_BANNER)
&& parse_string(&buffer, &length, &msg)
&& parse_string(&buffer, &language_length, &language)
&& parse_eod(&buffer))
{
/* Ignore language tag */
werror_utf8(length, msg);
lsh_string_free(packet);
return LSH_OK | LSH_GOON;
}
lsh_free(packet);
return LSH_FAIL | LSH_DIE;
}
static struct packet_handler *make_success_handler(struct ssh_service *service)
{
struct success_handler *self;
NEW(self);
self->super.handler = do_userauth_success;
self->service = service;
return &self->super;
}
static struct packet_handler *
make_failure_handler(struct client_userauth *userauth)
{
struct failure_handler *self;
NEW(self);
self->super.handler = do_userauth_failure;
self->userauth = userauth;
return &self->super;
}
static struct packet_handler *make_banner_handler()
{
struct packet_handler *self;
NEW(self);
self->handler = do_userauth_banner;
return &self->super;
}
static int init_client_userauth(struct ssh_service *c,
struct ssh_connection *connection)
{
struct client_userauth *closure = (struct client_userauth *) c;
MDEBUG(closure);
connection->dispatch[SSH_MSG_USERAUTH_SUCCESS]
= make_success_handler(closure->service);
connection->dispatch[SSH_MSG_USERAUTH_FAILURE]
= make_failure_handler(closure);
connection->dispatch[SSH_MSG_USERAUTH_BANNER]
= make_banner_handler();
return send_passwd(closure, connection);
}
struct ssh_service *make_client_userauth(struct lsh_string username,