NetBSD/lib/libkstream/kstream.c

344 lines
7.7 KiB
C

/* $NetBSD: kstream.c,v 1.4 2002/05/26 17:04:44 wiz Exp $ */
/* Encrypted-stream implementation for MIT Kerberos.
Written by Ken Raeburn (Raeburn@Cygnus.COM).
Copyright (C) 1991, 1992 by Cygnus Support.
This file is distributed under the same terms as Kerberos.
For copying and distribution information, please see the file
<kerberosIV/mit-copyright.h>.
from: kstream.c,v 1.7 1996/06/02 07:37:27 ghudson Exp $
*/
/* Current assumptions:
* the encryption/decryption routine may change the size of the data
significantly, in either direction, and need not be consistent
about it. (e.g., a pipe to "compress" could be used.)
* encryption/decryption may not consume all characters passed, but
will consume some, or will return a negative number indicating how
many more characters should be read before it will be able to do
anything with the data. (A return value of -1 is always safe; so
is calling the crypt routine again without enough data.)
* the file descriptor used is not set to non-blocking.
* no other routines will do i/o on that file descriptor while this
library is using it. (out-of-band messages are probably okay.)
Bugs:
* if the encryption package cannot handle arbitrarily small data,
flushing the outgoing stream may not work.
ToDo: Lots.
* completion of the code...
* begin testing
* buffering (efficiency)
* reduce data copying for large buffers that get sent immediately
* error handling
* consistency in error reporting conventions
*/
#include <assert.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
/* Only use alloca if we've got gcc 2 or better */
#ifdef __GNUC__
#if __GNUC__ >= 2
#define alloca __builtin_alloca
#define HAS_ALLOCA
#endif
#endif
#include <kerberosIV/kstream.h>
int krb_net_write (int, char *, int);
int krb_net_read (int, char *, int);
#ifdef sun
#ifndef solaris20
/* SunOS has no memmove, but bcopy overlaps correctly */
#define memmove(dest,src,size) bcopy(src,dest,size)
#endif
#endif
static void fifo__init (fifo *this)
{
this->next_write = this->next_read = 0;
memset (this->data, 0, sizeof (this->data));
}
static char *fifo__data_start (fifo *this)
{
return this->data + this->next_read;
}
static size_t fifo__bytes_available (fifo *this)
{
return this->next_write - this->next_read;
}
static size_t fifo__space_available (fifo *this)
{
return sizeof (this->data) - fifo__bytes_available (this);
}
static int fifo__append (fifo *this, const char *ptr, size_t len)
{
if (len > fifo__space_available (this))
len = fifo__space_available (this);
if (sizeof (this->data) - this->next_write < len)
{
memmove (this->data, this->data + this->next_read,
this->next_write - this->next_read);
this->next_write -= this->next_read;
this->next_read = 0;
}
memcpy (this->data + this->next_write, ptr, len);
this->next_write += len;
return len;
}
static int fifo__extract (fifo *this, char *ptr, size_t len)
{
size_t n = fifo__bytes_available (this);
if (len > n)
len = n;
if (ptr)
memcpy (ptr, this->data + this->next_read, len);
this->next_read += len;
if (this->next_read == this->next_write)
this->next_read = this->next_write = 0;
return len;
}
static void kstream_rec__init (kstream_rec *this)
{
fifo__init (&this->in_crypt);
fifo__init (&this->in_clear);
fifo__init (&this->out_clear);
}
kstream
kstream_create_from_fd (int fd, const struct kstream_crypt_ctl_block *ctl,
void *data)
{
kstream k;
k = (kstream) malloc (sizeof (kstream_rec));
if (!k)
return 0;
kstream_rec__init (k);
k->ctl = ctl;
k->data = 0;
k->fd = fd;
k->buffering = 1; /* why not? */
if (ctl && ctl->init && (ctl->init) (k, data) != 0)
{
free (k);
return 0;
}
return k;
}
int
kstream_destroy (kstream k)
{
int x = kstream_flush (k);
if (k->ctl && k->ctl->destroy)
(k->ctl->destroy) (k);
free (k);
return x;
}
void
kstream_set_buffer_mode (kstream k, int mode)
{
k->buffering = mode;
}
int
kstream_write (kstream k, kstream_ptr p_data, size_t p_len)
{
size_t len = p_len;
char *data = p_data;
int x = 0;
fifo *out = &k->out_clear;
assert (k != 0);
while (len)
{
x = fifo__append (out, data, len);
assert (x >= 0);
data += x;
len -= x;
if (len == 0 && k->buffering != 0)
return p_len;
x = kstream_flush (k);
if (x < 0)
return x;
}
return p_len;
}
int
kstream_flush (kstream k)
{
int x, n;
fifo *out = &k->out_clear;
struct kstream_data_block kd_out, kd_in;
assert (k != 0);
if (k->ctl == 0)
{
n = fifo__bytes_available (out);
x = krb_net_write (k->fd, fifo__data_start (out), n);
if (x < 0)
return x;
else if (x != n)
abort ();
fifo__extract (out, 0, n);
return 0;
}
n = fifo__bytes_available (out);
kd_in.length = n;
kd_in.ptr = fifo__data_start (out);
kd_out.ptr = 0;
kd_out.length = 0;
while (fifo__bytes_available (out))
{
x = (k->ctl->encrypt) (&kd_out, &kd_in, k);
if (x < 0)
return x;
else if (x == 0)
return -1;
/* x is number of input characters processed */
fifo__extract (out, 0, x);
x = krb_net_write (k->fd, kd_out.ptr, kd_out.length);
if (x < 0)
return x;
else if (x != kd_out.length)
abort ();
}
return 0;
}
int
kstream_read (kstream k, kstream_ptr p_data, size_t p_len)
{
char *data = p_data;
size_t len = p_len;
int n;
fifo *in = &k->in_clear, *cryptstr;
struct kstream_data_block kd_out, kd_in;
assert (k != 0);
read_clear:
if (k->ctl == 0)
return read (k->fd, data, len);
if (fifo__bytes_available (in) > 0)
return fifo__extract (in, data, len);
cryptstr = &k->in_crypt;
try_2:
kd_out.ptr = 0;
kd_out.length = 0;
kd_in.length = fifo__bytes_available (cryptstr);
kd_in.ptr = fifo__data_start (cryptstr);
if (kd_in.length == 0)
{
n = -1;
goto read_source;
}
n = (k->ctl->decrypt) (&kd_out, &kd_in, k);
if (n > 0)
{
/* Succeeded in decrypting some data. */
fifo__extract (cryptstr, 0, n);
{
int n2;
n = kd_out.length;
n2 = fifo__append (in, kd_out.ptr, kd_out.length);
assert (n == n2);
}
goto read_clear;
}
else if (n == 0)
assert (n != 0); /* not handling errors yet */
read_source:
{
size_t sz;
static char *buf;
sz = -n;
if (sz > fifo__space_available (cryptstr))
{
#ifdef DEBUG
fprintf (stderr, "insufficient space avail (%d, want %d)\n",
fifo__space_available (cryptstr), sz);
#endif
errno = ENOMEM;
return -1;
}
#ifdef HAS_ALLOCA
buf = alloca (sz);
#else
if (buf)
buf = realloc (buf, sz);
else
buf = malloc (sz);
assert(buf);
#endif
while (sz > 0)
{
int n2;
n2 = krb_net_read (k->fd, buf, sz);
if (n2 < 0)
{
#ifdef DEBUG
perror ("kstream_read: krb_net_read");
#endif
return n2;
}
else if (n2 == 0)
return 0;
fifo__append (cryptstr, buf, n2);
sz -= n2;
}
goto try_2;
}
}
#if 0
extern "C" {
/* simple rotate */
typedef struct kstream_data_block DB;
static int
move (DB *out, DB *in, kstream k, int direction)
{
int i, x;
static char *ptr;
char *inp = in->ptr;
if (in->length == 0) return -1;
/* Sun's realloc loses on null pointers. */
ptr = ptr ? realloc (ptr, in->length) : malloc (in->length);
out->ptr = ptr;
if (!ptr) return 0;
out->length = in->length;
x = direction ? +1 : -1;
for (i = in->length; i-- > 0; )
ptr[i] = inp[i] + x;
return in->length;
}
static int emove (DB *out, DB *in, kstream k) { return move (out, in, k, 0); }
static int dmove (DB *out, DB *in, kstream k) { return move (out, in, k, 1); }
extern const struct kstream_crypt_ctl_block rot1_ccb = {
emove, dmove, 0, 0
};
}
#endif