aboutsummaryrefslogtreecommitdiff
path: root/src/appl/gssftp/ftp/secure.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/appl/gssftp/ftp/secure.c')
-rw-r--r--src/appl/gssftp/ftp/secure.c382
1 files changed, 382 insertions, 0 deletions
diff --git a/src/appl/gssftp/ftp/secure.c b/src/appl/gssftp/ftp/secure.c
new file mode 100644
index 0000000..4451339
--- /dev/null
+++ b/src/appl/gssftp/ftp/secure.c
@@ -0,0 +1,382 @@
+/*
+ * Shared routines for client and server for
+ * secure read(), write(), getc(), and putc().
+ * Only one security context, thus only work on one fd at a time!
+ */
+
+#include "secure.h" /* stuff which is specific to client or server */
+
+#ifdef KERBEROS
+#include <krb.h>
+
+CRED_DECL
+extern KTEXT_ST ticket;
+extern MSG_DAT msg_data;
+extern Key_schedule schedule;
+#endif /* KERBEROS */
+#ifdef GSSAPI
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_generic.h>
+extern gss_ctx_id_t gcontext;
+#endif /* GSSAPI */
+
+#include <arpa/ftp.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <errno.h>
+extern int errno;
+extern char *sys_errlist[];
+
+extern struct sockaddr_in hisaddr;
+extern struct sockaddr_in myaddr;
+extern int level;
+extern char *auth_type;
+
+#define MAX maxbuf
+extern unsigned int maxbuf; /* maximum output buffer size */
+extern unsigned char *ucbuf; /* cleartext buffer */
+static unsigned int nout, bufp; /* number of chars in ucbuf,
+ * pointer into ucbuf */
+
+#ifdef KERBEROS
+#define FUDGE_FACTOR 32 /* Amount of growth
+ * from cleartext to ciphertext.
+ * krb_mk_priv adds this # bytes.
+ * Must be defined for each auth type.
+ */
+#endif /* KERBEROS */
+
+#ifndef FUDGE_FACTOR /* In case no auth types define it. */
+#define FUDGE_FACTOR 0
+#endif
+
+#ifdef KERBEROS
+/* XXX - The following must be redefined if KERBEROS_V4 is not used
+ * but some other auth type is. They must have the same properties. */
+#define looping_write krb_net_write
+#define looping_read krb_net_read
+#endif
+
+/* perhaps use these in general, certainly use them for GSSAPI */
+
+#ifndef looping_write
+static int
+looping_write(fd, buf, len)
+ int fd;
+ register const char *buf;
+ int len;
+{
+ int cc;
+ register int wrlen = len;
+ do {
+ cc = write(fd, buf, wrlen);
+ if (cc < 0) {
+ if (errno == EINTR)
+ continue;
+ return(cc);
+ }
+ else {
+ buf += cc;
+ wrlen -= cc;
+ }
+ } while (wrlen > 0);
+ return(len);
+}
+#endif
+#ifndef looping_read
+static int
+looping_read(fd, buf, len)
+ int fd;
+ register char *buf;
+ register int len;
+{
+ int cc, len2 = 0;
+
+ do {
+ cc = read(fd, buf, len);
+ if (cc < 0) {
+ if (errno == EINTR)
+ continue;
+ return(cc); /* errno is already set */
+ }
+ else if (cc == 0) {
+ return(len2);
+ } else {
+ buf += cc;
+ len2 += cc;
+ len -= cc;
+ }
+ } while (len > 0);
+ return(len2);
+}
+#endif
+
+
+#if defined(STDARG) || (defined(__STDC__) && ! defined(VARARGS))
+extern secure_error(char *, ...);
+#else
+extern secure_error();
+#endif
+
+#define ERR -2
+
+static
+secure_putbyte(fd, c)
+int fd;
+unsigned char c;
+{
+ int ret;
+
+ ucbuf[nout++] = c;
+ if (nout == MAX - FUDGE_FACTOR)
+ if (ret = secure_putbuf(fd, ucbuf, nout))
+ return(ret);
+ else nout = 0;
+ return(c);
+}
+
+/* returns:
+ * 0 on success
+ * -1 on error (errno set)
+ * -2 on security error
+ */
+secure_flush(fd)
+int fd;
+{
+ int ret;
+
+ if (level == PROT_C)
+ return(0);
+ if (nout)
+ if (ret = secure_putbuf(fd, ucbuf, nout))
+ return(ret);
+ return(secure_putbuf(fd, "", nout = 0));
+}
+
+/* returns:
+ * c>=0 on success
+ * -1 on error
+ * -2 on security error
+ */
+secure_putc(c, stream)
+char c;
+FILE *stream;
+{
+ if (level == PROT_C)
+ return(putc(c,stream));
+ return(secure_putbyte(fileno(stream), (unsigned char) c));
+}
+
+/* returns:
+ * nbyte on success
+ * -1 on error (errno set)
+ * -2 on security error
+ */
+secure_write(fd, buf, nbyte)
+int fd;
+unsigned char *buf;
+unsigned int nbyte;
+{
+ unsigned int i;
+ int c;
+
+ if (level == 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);
+}
+
+/* returns:
+ * 0 on success
+ * -1 on error (errno set)
+ * -2 on security error
+ */
+secure_putbuf(fd, buf, nbyte)
+int fd;
+unsigned char *buf;
+unsigned int nbyte;
+{
+ static char *outbuf; /* output ciphertext */
+ static unsigned int bufsize; /* size of outbuf */
+ long length;
+ u_long net_len;
+
+ if (bufsize < nbyte + FUDGE_FACTOR) {
+ if (outbuf) (void) free(outbuf);
+ if (outbuf = malloc((unsigned) (nbyte + FUDGE_FACTOR)))
+ bufsize = nbyte + FUDGE_FACTOR;
+ else {
+ bufsize = 0;
+ secure_error("%s (in malloc of PROT buffer)",
+ sys_errlist[errno]);
+ return(ERR);
+ }
+ }
+ /* Other auth types go here ... */
+#ifdef KERBEROS
+ if (strcmp(auth_type, "KERBEROS_V4") == 0)
+ if ((length = level == PROT_P ?
+ krb_mk_priv(buf, (unsigned char *) outbuf, nbyte, schedule,
+ SESSION, &myaddr, &hisaddr)
+ : krb_mk_safe(buf, (unsigned char *) outbuf, nbyte, SESSION,
+ &myaddr, &hisaddr)) == -1) {
+ secure_error("krb_mk_%s failed for KERBEROS_V4",
+ level == PROT_P ? "priv" : "safe");
+ return(ERR);
+ }
+#endif /* KERBEROS */
+#ifdef GSSAPI
+ if (strcmp(auth_type, "GSSAPI") == 0) {
+ 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,
+ (level == PROT_P), /* confidential */
+ GSS_C_QOP_DEFAULT,
+ &in_buf, &conf_state,
+ &out_buf);
+ if (maj_stat != GSS_S_COMPLETE) {
+ /* generally need to deal */
+ /* ie. should loop, but for now just fail */
+ secure_gss_error(maj_stat, min_stat,
+ level == PROT_P?
+ "GSSAPI seal failed":
+ "GSSAPI sign failed");
+ return(ERR);
+ }
+ memcpy(outbuf, out_buf.value, length=out_buf.length);
+ gss_release_buffer(&min_stat, &out_buf);
+
+ }
+#endif /* GSSAPI */
+ net_len = htonl((u_long) length);
+ if (looping_write(fd, &net_len, sizeof(net_len)) == -1) return(-1);
+ if (looping_write(fd, outbuf, length) != length) return(-1);
+ return(0);
+}
+
+static
+secure_getbyte(fd)
+int fd;
+{
+ /* number of chars in ucbuf, pointer into ucbuf */
+ static unsigned int nin, bufp;
+ int kerror;
+ u_long length;
+
+ if (nin == 0) {
+ if ((kerror = looping_read(fd, &length, sizeof(length)))
+ != sizeof(length)) {
+ secure_error("Couldn't read PROT buffer length: %d/%s",
+ kerror,
+ kerror == -1 ? sys_errlist[errno]
+ : "premature EOF");
+ return(ERR);
+ }
+ if ((length = (u_long) ntohl(length)) > MAX) {
+ secure_error("Length (%d) of PROT buffer > PBSZ=%u",
+ length, MAX);
+ return(ERR);
+ }
+ if ((kerror = looping_read(fd, ucbuf, length)) != length) {
+ secure_error("Couldn't read %u byte PROT buffer: %s",
+ length, kerror == -1 ?
+ sys_errlist[errno] : "premature EOF");
+ return(ERR);
+ }
+ /* Other auth types go here ... */
+#ifdef KERBEROS
+ if (strcmp(auth_type, "KERBEROS_V4") == 0) {
+ if (kerror = level == PROT_P ?
+ krb_rd_priv(ucbuf, length, schedule, SESSION,
+ &hisaddr, &myaddr, &msg_data)
+ : krb_rd_safe(ucbuf, length, SESSION,
+ &hisaddr, &myaddr, &msg_data)) {
+ secure_error("krb_rd_%s failed for KERBEROS_V4 (%s)",
+ level == PROT_P ? "priv" : "safe",
+ krb_get_err_text(kerror));
+ return(ERR);
+ }
+ memcpy(ucbuf, msg_data.app_data, msg_data.app_length);
+ nin = bufp = msg_data.app_length;
+ }
+#endif /* KERBEROS */
+#ifdef GSSAPI
+ if (strcmp(auth_type, "GSSAPI") == 0) {
+ 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 = (level == PROT_P);
+ /* decrypt/verify the message */
+ maj_stat = gss_unseal(&min_stat, gcontext, &xmit_buf,
+ &msg_buf, &conf_state, NULL);
+ if (maj_stat != GSS_S_COMPLETE) {
+ secure_gss_error(maj_stat, min_stat,
+ (level == 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);
+ }
+#endif /* GSSAPI */
+ /* Other auth types go here ... */
+ }
+ if (nin == 0)
+ return(EOF);
+ else return(ucbuf[bufp - nin--]);
+}
+
+/* returns:
+ * c>=0 on success
+ * -1 on EOF
+ * -2 on security error
+ */
+secure_getc(stream)
+FILE *stream;
+{
+ if (level == PROT_C)
+ return(getc(stream));
+ return(secure_getbyte(fileno(stream)));
+}
+
+/* returns:
+ * n>0 on success (n == # of bytes read)
+ * 0 on EOF
+ * -1 on error (errno set), only for PROT_C
+ * -2 on security error
+ */
+secure_read(fd, buf, nbyte)
+int fd;
+char *buf;
+int nbyte;
+{
+ static int c;
+ int i;
+
+ if (level == PROT_C)
+ return(read(fd,buf,nbyte));
+ if (c == EOF)
+ return(c = 0);
+ for (i=0; nbyte>0; nbyte--)
+ switch (c = secure_getbyte(fd)) {
+ case ERR: return(c);
+ case EOF: if (!i) c = 0;
+ return(i);
+ default: buf[i++] = c;
+ }
+ return(i);
+}