#include "ftp_var.h"
#include <gssapi/gssapi.h>
#include <arpa/ftp.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <errno.h>
extern struct sockaddr_in hisaddr;
extern struct sockaddr_in myaddr;
extern int dlevel;
extern int auth_type;
extern uint_t maxbuf;
extern uchar_t *ucbuf;
static uint_t nout;
static uint_t smaxbuf;
static uint_t smaxqueue;
extern gss_ctx_id_t gcontext;
static int secure_putbuf(int, uchar_t *, uint_t);
static int
looping_write(int fd, const char *buf, int len)
{
int cc, len2 = 0;
if (len == 0)
return (0);
do {
cc = write(fd, buf, len);
if (cc < 0) {
if (errno == EINTR)
continue;
return (cc);
} else if (cc == 0) {
return (len2);
} else {
buf += cc;
len2 += cc;
len -= cc;
}
} while (len > 0);
return (len2);
}
static int
looping_read(int fd, char *buf, int len)
{
int cc, len2 = 0;
do {
cc = read(fd, buf, len);
if (cc < 0) {
if (errno == EINTR)
continue;
return (cc);
} else if (cc == 0) {
return (len2);
} else {
buf += cc;
len2 += cc;
len -= cc;
}
} while (len > 0);
return (len2);
}
#define ERR -2
static void
secure_error(char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
putc('\n', stderr);
}
static int
secure_determine_constants(void)
{
smaxbuf = maxbuf;
smaxqueue = maxbuf;
if (auth_type == AUTHTYPE_GSSAPI) {
OM_uint32 maj_stat, min_stat, mlen;
OM_uint32 msize = maxbuf;
maj_stat = gss_wrap_size_limit(&min_stat, gcontext,
(dlevel == PROT_P),
GSS_C_QOP_DEFAULT,
msize, &mlen);
if (maj_stat != GSS_S_COMPLETE) {
user_gss_error(maj_stat, min_stat,
"GSSAPI fudge determination");
return (ERR);
}
smaxqueue = mlen;
}
return (0);
}
static uchar_t
secure_putbyte(int fd, uchar_t c)
{
int ret;
if ((smaxbuf == 0) || (smaxqueue == 0) || (smaxbuf != maxbuf)) {
ret = secure_determine_constants();
if (ret)
return (ret);
}
ucbuf[nout++] = c;
if (nout == smaxqueue) {
nout = 0;
ret = secure_putbuf(fd, ucbuf, smaxqueue);
return (ret ? ret :c);
}
return (c);
}
int
secure_flush(int fd)
{
int ret;
if (dlevel == PROT_C)
return (0);
if (nout)
if (ret = secure_putbuf(fd, ucbuf, nout))
return (ret);
return (secure_putbuf(fd, (uchar_t *)"", nout = 0));
}
int
secure_putc(int c, FILE *stream)
{
if (dlevel == PROT_C)
return (putc(c, stream));
return (secure_putbyte(fileno(stream), (uchar_t)c));
}
ssize_t
secure_write(int fd, const void *inbuf, size_t nbyte)
{
uint_t i;
int c;
uchar_t *buf = (uchar_t *)inbuf;
if (dlevel == PROT_C)
return (write(fd, buf, nbyte));
for (i = 0; nbyte > 0; nbyte--)
if ((c = secure_putbyte(fd, buf[i++])) < 0)
return (c);
return (i);
}
static int secure_putbuf(int fd, uchar_t *buf, uint_t nbyte)
{
static char *outbuf;
static uint_t bufsize;
int length;
uint_t net_len;
if (auth_type == AUTHTYPE_GSSAPI) {
gss_buffer_desc in_buf, out_buf;
OM_uint32 maj_stat, min_stat;
int conf_state;
in_buf.value = buf;
in_buf.length = nbyte;
maj_stat = gss_seal(&min_stat, gcontext,
(dlevel == PROT_P),
GSS_C_QOP_DEFAULT,
&in_buf, &conf_state,
&out_buf);
if (maj_stat != GSS_S_COMPLETE) {
user_gss_error(maj_stat, min_stat, dlevel == PROT_P?
"GSSAPI seal failed" : "GSSAPI sign failed");
return (ERR);
}
if (bufsize < out_buf.length) {
outbuf = outbuf ?
realloc(outbuf, (size_t)out_buf.length) :
malloc((size_t)out_buf.length);
if (outbuf)
bufsize = out_buf.length;
else {
bufsize = 0;
secure_error("%s (in malloc of PROT buffer)",
strerror(errno));
return (ERR);
}
}
memcpy(outbuf, out_buf.value, length = out_buf.length);
gss_release_buffer(&min_stat, &out_buf);
}
net_len = htonl((uint32_t)length);
if (looping_write(fd, (char *)&net_len, 4) == -1)
return (-1);
if (looping_write(fd, outbuf, length) != length)
return (-1);
return (0);
}
static int
secure_getbyte(int fd)
{
static uint_t nin, bufp;
int kerror;
uint_t length;
if (nin == 0) {
if ((kerror =
looping_read(fd, (char *)&length, sizeof (length)))
!= sizeof (length)) {
secure_error("Couldn't read PROT buffer length: %d/%s",
kerror, (kerror == -1) ? strerror(errno) :
"premature EOF");
return (ERR);
}
if ((length = ntohl((uint32_t)length)) > maxbuf) {
secure_error("Length (%d) of PROT buffer > PBSZ=%u",
length, maxbuf);
return (ERR);
}
if ((kerror = looping_read(fd, (char *)ucbuf, length))
!= length) {
secure_error("Couldn't read %u byte PROT buffer: %s",
length, kerror == -1 ?
strerror(errno) : "premature EOF");
return (ERR);
}
if (auth_type == AUTHTYPE_GSSAPI) {
gss_buffer_desc xmit_buf, msg_buf;
OM_uint32 maj_stat, min_stat;
int conf_state;
xmit_buf.value = ucbuf;
xmit_buf.length = length;
conf_state = (dlevel == PROT_P);
maj_stat = gss_unseal(&min_stat, gcontext, &xmit_buf,
&msg_buf, &conf_state, NULL);
if (maj_stat != GSS_S_COMPLETE) {
user_gss_error(maj_stat, min_stat,
(dlevel == PROT_P)?
"failed unsealing ENC message":
"failed unsealing MIC message");
return (ERR);
}
memcpy(ucbuf, msg_buf.value,
nin = bufp = msg_buf.length);
gss_release_buffer(&min_stat, &msg_buf);
}
}
return ((nin == 0) ? EOF : ucbuf[bufp - nin--]);
}
int
secure_getc(FILE *stream)
{
if (dlevel == PROT_C)
return (getc(stream));
return (secure_getbyte(fileno(stream)));
}
ssize_t
secure_read(int fd, void *inbuf, size_t nbyte)
{
int c, i;
char *buf = (char *)inbuf;
if (dlevel == PROT_C)
return (read(fd, buf, nbyte));
if (goteof)
return (goteof = 0);
for (i = 0; nbyte > 0; nbyte--)
switch (c = secure_getbyte(fd)) {
case ERR:
return (c);
case EOF:
goteof = i ? 1 : 0;
return (i);
default:
buf[i++] = c;
}
return (i);
}