/* $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 . 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 #include #include #include #include #include /* 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 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