changeset 21397:aa5d46f74b6a

Integrate support for GSS-API authentication, implemented by Kevin L. Mitchel <klmitch@mit.edu>.
author Karl Heuer <kwzh@gnu.org>
date Mon, 06 Apr 1998 22:16:30 +0000
parents fe54b9d8ded1
children 625ff8638146
files lib-src/Makefile.in lib-src/pop.c lib-src/pop.h
diffstat 3 files changed, 1016 insertions(+), 24 deletions(-) [+]
line wrap: on
line diff
--- a/lib-src/Makefile.in	Mon Apr 06 22:10:30 1998 +0000
+++ b/lib-src/Makefile.in	Mon Apr 06 22:16:30 1998 +0000
@@ -205,12 +205,21 @@
 # ifdef HAVE_LIBCOM_ERR
 	COM_ERRLIB = -lcom_err
 # endif
-#endif /* KERBEROS
+# ifdef HAVE_LIBGSSAPI_KRB5
+	GSSAPI_KRB5LIB = -lgssapi_krb5
+# endif
+#endif /* KERBEROS */
 
-/* If HESIOD is defined, set this to "-lhesiod". */
-HESIODLIB=
+#ifdef HAVE_LIBGSSAPI
+	GSSAPILIB = -lgssapi
+#endif
 
-MOVE_LIBS=$(KRB4LIB) $(DESLIB) $(KRB5LIB) $(CRYPTOLIB) $(COM_ERRLIB) $(HESIODLIB)
+#ifdef HESIOD
+HESIODLIB= -lhesiod
+#endif
+
+MOVE_LIBS=$(GSSAPI_KRB5LIB) $(GSSAPILIB) $(KRB4LIB) $(DESLIB) $(KRB5LIB) \
+	$(CRYPTOLIB) $(COM_ERRLIB) $(HESIODLIB)
 
 #ifdef HAVE_LIBMAIL
 LIBMAIL=-lmail
--- a/lib-src/pop.c	Mon Apr 06 22:10:30 1998 +0000
+++ b/lib-src/pop.c	Mon Apr 06 22:16:30 1998 +0000
@@ -161,6 +161,57 @@
 #define KPOP_SERVICE "kpop"
 #endif
 
+#ifdef GSSAPI
+# ifdef HAVE_GSSAPI_H
+#  include <gssapi.h>
+# else
+#  include <gssapi/gssapi.h>
+# endif
+#define GSSAPI_SERVICE "pop"
+static int pop_auth (/* popserver server, char *user,
+			char *host, int flags */);
+static void gen_gss_error (/* char *msg, OM_uint32 major, OM_uint32 minor */);
+struct _pop_gssapi
+{
+  int gss_flags;		/* encryption?  integrity protection? */
+  OM_uint32 max_size;		/* max size we can send the server */
+  gss_ctx_id_t gss_context;	/* the security context */
+};
+#define GSSAPI_NOPROT		0x01
+#define GSSAPI_INTEGRITY	0x02
+#define GSSAPI_PRIVACY		0x04
+#define GSSAPI_NEEDWRAP		(GSSAPI_INTEGRITY|GSSAPI_PRIVACY)
+#define GSSAPI_PROTECTION	(GSSAPI_NOPROT|GSSAPI_INTEGRITY|GSSAPI_PRIVACY)
+#define GSSAPI_RCVBUF		1024
+#define GSSAPI_SVC_TYPE	{10, "\052\206\110\206\367\022\001\002\001\004"}
+#define Gssapi(data)		((struct _pop_gssapi *) (data))
+
+static int b64_decode (/* char *enc, gss_buffer_t dec */);
+static int b64_encode (/* gss_buffer_t dec, char **enc */);
+#define B64_SUCCESS	0
+#define B64_BADPARAM	1
+#define B64_BADCHAR	2
+#define B64_BADPAD	3
+#define B64_BADLEN	4
+#define B64_NOMEM	5
+static char *b64_error[] =
+{
+  "Success",
+  "Bad parameters",
+  "Bad characters in encoding",
+  "Bad padding in encoding",
+  "Bad length",
+  "Out of memory"
+};
+
+/*
+ * This function is only needed if you are using the GSSAPI protection
+ * mechanisms; it keeps trying until it has read the requested number
+ * bytes from the passed-in fd.
+ */
+static int fullread (/* int fd, char *buf, int nbytes */);
+#endif /* GSSAPI */
+
 char pop_error[ERROR_MAX];
 int pop_debug = 0;
 
@@ -269,10 +320,22 @@
     }
 
   /* Determine the password */
-#ifdef KERBEROS
-#define DONT_NEED_PASSWORD (! (flags & POP_NO_KERBEROS))
+#if defined(KERBEROS) || defined(GSSAPI)
+# ifdef KERBEROS
+#  define NO_KERBEROS	POP_NO_KERBEROS
+# else
+#  define NO_KERBEROS	0
+# endif /* KERBEROS */
+
+# ifdef GSSAPI
+#  define NO_GSSAPI	POP_NO_GSSAPI
+# else
+#  define NO_GSSAPI	0
+# endif /* GSSAPI */
+
+# define DONT_NEED_PASSWORD (! (flags & (NO_KERBEROS | NO_GSSAPI)))
 #else
-#define DONT_NEED_PASSWORD 0
+# define DONT_NEED_PASSWORD 0
 #endif
  
   if ((! password) && (! DONT_NEED_PASSWORD))
@@ -288,7 +351,7 @@
 	}
     }
   if (password)
-    flags |= POP_NO_KERBEROS;
+    flags |= POP_NO_KERBEROS | (!(flags & POP_NO_NOPROT) ? POP_NO_GSSAPI : 0);
   else
     password = username;
 
@@ -316,10 +379,46 @@
   server->buffer_size = GETLINE_MIN;
   server->in_multi = 0;
   server->trash_started = 0;
+  server->extra = 0;
 
   if (getok (server))
     return (0);
 
+#ifdef GSSAPI
+  /*
+   * unless forbidden to use GSSAPI, try the GSSAPI AUTH mechanism..first.
+   */
+  pop_error[0] = '\0'; /* so we can detect errors later... */
+  if (! (flags & POP_NO_GSSAPI))
+    {
+      int ret;
+
+      ret = pop_auth (server, username, host, flags);
+      if (ret == 0)
+	{
+	  return (server);
+	}
+      else if (ret == -2)
+	{
+	  pop_close (server);
+	  return (0);
+	}
+    }
+#endif /* GSSAPI */
+  /*
+   * POP_NO_NOPROT is used in the case that we want protection; if
+   * the authentication negotiation failed, then we want to fail now.
+   */
+  if ((flags & POP_NO_NOPROT))
+    {
+      pop_close (server);
+#ifdef GSSAPI
+      if (pop_error[0] == '\0')
+#endif
+	strcpy (pop_error, "Unable to provide protection");
+      return (0);
+    }
+
   /*
    * I really shouldn't use the pop_error variable like this, but....
    */
@@ -1004,6 +1103,17 @@
 
   if (server->buffer)
     free (server->buffer);
+#ifdef GSSAPI
+  if (server->extra)
+    {
+      OM_uint32 minor;
+
+      if (Gssapi (server->extra)->gss_context != GSS_C_NO_CONTEXT)
+	gss_delete_sec_context (&minor, &(Gssapi (server->extra)->gss_context),
+				GSS_C_NO_BUFFER);
+      free ((char *) server->extra);
+    }
+#endif /* GSSAPI */
   free ((char *) server);
 
   return (ret);
@@ -1332,22 +1442,102 @@
 
   while (1)
     {
-      /* There's a "- 1" here to leave room for the null that we put
-         at the end of the read data below.  We put the null there so
-         that find_crlf knows where to stop when we call it. */
-      if (server->data == server->buffer_size - 1)
+#ifdef GSSAPI
+      /*
+       * We might be playing with a protected connection.  If we are, then
+       * we need to first read a chunk of ciphertext from the server,
+       * unwrap it, and stuff it into the buffer.
+       */
+      if (server->extra &&
+	  ((Gssapi (server->extra)->gss_flags) & GSSAPI_NEEDWRAP))
 	{
-	  server->buffer_size += GETLINE_INCR;
-	  server->buffer = (char *)realloc (server->buffer, server->buffer_size);
-	  if (! server->buffer)
+	  char rcvbuf[GSSAPI_RCVBUF];
+	  OM_uint32 major, minor, length;
+	  gss_buffer_desc in_tok, out_tok;
+	  struct _pop_gssapi *gss_data = Gssapi (server->extra);
+
+	  ret = fullread (server->file, (char *) &length, sizeof (length));
+
+	  if (ret == sizeof (length))
 	    {
-	      strcpy (pop_error, "Out of memory in pop_getline");
-	      pop_trash (server);
-	      return (-1);
+	      in_tok.length = ntohl (length);
+
+	      if (in_tok.length <= GSSAPI_RCVBUF)
+		{
+		  ret = fullread (server->file, rcvbuf, in_tok.length);
+
+		  if (ret == in_tok.length)
+		    {
+		      in_tok.value = (void *) rcvbuf;
+
+		      major = gss_unwrap (&minor, gss_data->gss_context,
+					  &in_tok, &out_tok, 0, 0);
+
+		      if (major != GSS_S_COMPLETE)
+			{
+			  pop_trash (server);
+			  gen_gss_error ("unwrapping", major, minor);
+			  return (-1);
+			}
+
+		      while (server->data + out_tok.length >=
+			     server->buffer_size - 1)
+			server->buffer_size += GETLINE_INCR;
+
+		      server->buffer = (char *)realloc (server->buffer,
+							server->buffer_size);
+
+		      if (! server->buffer)
+			{
+			  gss_release_buffer (&minor, &out_tok);
+			  pop_trash (server);
+			  strcpy (pop_error, "Out of memory in pop_getline");
+			  return (-1);
+			}
+
+		      bcopy (out_tok.value, server->buffer + server->data,
+			     out_tok.length);
+
+		      ret = out_tok.length;
+
+		      gss_release_buffer (&minor, &out_tok);
+		    }
+		  else
+		    ret = 0;	/* force detection of unexpected EOF */
+		}
+	      else
+		{
+		  pop_trash (server);
+		  strcpy (pop_error, "Token from server too long in pop_getline");
+		  return (-1);
+		}
 	    }
+	  else
+	    ret = 0;		/* force detection of unexpected EOF */
 	}
-      ret = RECV (server->file, server->buffer + server->data,
-		  server->buffer_size - server->data - 1, 0);
+      else
+	{
+#endif /* GSSAPI */
+	  /* There's a "- 1" here to leave room for the null that we put
+	     at the end of the read data below.  We put the null there so
+	     that find_crlf knows where to stop when we call it. */
+	  if (server->data == server->buffer_size - 1)
+	    {
+	      server->buffer_size += GETLINE_INCR;
+	      server->buffer = (char *)realloc (server->buffer,
+						server->buffer_size);
+	      if (! server->buffer)
+		{
+		  strcpy (pop_error, "Out of memory in pop_getline");
+		  pop_trash (server);
+		  return (-1);
+		}
+	    }
+	  ret = RECV (server->file, server->buffer + server->data,
+		      server->buffer_size - server->data - 1, 0);
+#ifdef GSSAPI
+	}
+#endif /* GSSAPI */
       if (ret < 0)
 	{
 	  strcpy (pop_error, GETLINE_ERROR);
@@ -1391,6 +1581,37 @@
   /* NOTREACHED */
 }
 
+#ifdef GSSAPI
+/*
+ * Function: fullread
+ *
+ * Purpose: Just like read, but keeps trying until the specified number
+ * 	number of bytes has been read into the buffer.  This function is
+ * 	only needed if you are using the GSSAPI protection mechanisms.
+ *
+ * Return value: Same as read.  Pop_error is not set.
+ */
+static int
+fullread (fd, buf, nbytes)
+     int fd;
+     char *buf;
+     int nbytes;
+{
+  char *cp;
+  int ret;
+
+  cp = buf;
+
+  while (nbytes > 0 && (ret = RECV (fd, cp, nbytes, 0)) > 0)
+    {
+      cp += ret;
+      nbytes -= ret;
+    }
+
+  return (ret);
+}
+#endif /* GSSAPI */
+
 /*
  * Function: sendline
  *
@@ -1417,11 +1638,87 @@
 #define SENDLINE_ERROR "Error writing to POP server: "
   int ret;
 
-  ret = fullwrite (server->file, line, strlen (line));
-  if (ret >= 0)
-    {				/* 0 indicates that a blank line was written */
-      ret = fullwrite (server->file, "\r\n", 2);
+#ifdef GSSAPI
+  /*
+   * We might be playing with a protected connection.  If we are, then we
+   * need to build our full plaintext, parse it into chunks small enough
+   * for the server to swallow, wrap each one, and send it over the net as
+   * specified by the RFC.
+   */
+  if (server->extra && ((Gssapi (server->extra)->gss_flags) & GSSAPI_NEEDWRAP))
+    {
+      char *sendbuf, *ptr;
+      OM_uint32 major, minor, length;
+      gss_buffer_desc in_tok, out_tok;
+      int len = 0, tot_len;
+      struct _pop_gssapi *gss_data = Gssapi (server->extra);
+
+      sendbuf = malloc (strlen (line) + 3);
+
+      if (! sendbuf)
+	{
+	  pop_trash (server);
+	  strcpy (pop_error, "Out of memory in sendline");
+	  return (-1);
+	}
+
+      tot_len = sprintf (sendbuf, "%s\r\n", line);
+
+      for (ptr = sendbuf; tot_len > 0; tot_len -= len, ptr += len)
+	{
+	  len = ((tot_len > gss_data->max_size) ?
+		 gss_data->max_size : tot_len);
+
+	  in_tok.value = (void *) ptr;
+	  in_tok.length = len;
+
+	  major = gss_wrap (&minor, gss_data->gss_context,
+			    (gss_data->gss_flags & GSSAPI_PRIVACY) ? 1 : 0,
+			    GSS_C_QOP_DEFAULT, &in_tok, 0, &out_tok);
+
+	  if (major != GSS_S_COMPLETE)
+	    {
+	      free (sendbuf);
+	      pop_trash (server);
+	      gen_gss_error ("wrapping", major, minor);
+	      return (-1);
+	    }
+
+	  /*
+	   * "Once the protection mechanism is in effect, the stream of
+	   *  command and response octets is processed into buffers of
+	   *  ciphertext.  Each buffer is transferred over the connection
+	   *  as a stream of octets prepended with a four octet field in
+	   *  network byte order that represents the length of the
+	   *  following data." - RFC 1734, section 2
+	   */
+	  length = htonl (out_tok.length);
+	  ret = fullwrite (server->file, (char *) &length, sizeof (length));
+	  if (ret == sizeof (length))
+	    {
+	      ret = fullwrite (server->file, (char *) out_tok.value,
+			       out_tok.length);
+	    }
+
+	  gss_release_buffer (&minor, &out_tok);
+
+	  if (ret < 0)
+	    break;
+	}
+
+      free (sendbuf);
     }
+  else
+    {
+#endif /* GSSAPI */
+      ret = fullwrite (server->file, line, strlen (line));
+      if (ret >= 0)
+	{			/* 0 indicates that a blank line was written */
+	  ret = fullwrite (server->file, "\r\n", 2);
+	}
+#ifdef GSSAPI
+    }
+#endif /* GSSAPI */
 
   if (ret < 0)
     {
@@ -1589,6 +1886,19 @@
 	  free (server->buffer);
 	  server->buffer = 0;
 	}
+#ifdef GSSAPI
+      if (server->extra)
+	{
+	  OM_uint32 minor;
+
+	  if (Gssapi (server->extra)->gss_context != GSS_C_NO_CONTEXT)
+	    gss_delete_sec_context (&minor,
+				    &(Gssapi (server->extra)->gss_context),
+				   GSS_C_NO_BUFFER);
+	  free ((char *) server->extra);
+	  server->extra = 0;
+	}
+#endif /* GSSAPI */
     }
 
 #ifdef WINDOWSNT
@@ -1597,6 +1907,654 @@
 #endif
 }
 
+#ifdef GSSAPI
+/*
+ * Function: pop_auth
+ *
+ * Purpose: To perform a GSSAPI authentication handshake with a POP server.
+ * 	If the negotiation is successful, it will return 0; otherwise, it
+ * 	will fill in pop_error with the error message and return either -1,
+ * 	indicating a potentially recoverable error, or -2, indicating an
+ * 	unrecoverable error.
+ *
+ * Side effects: The server may choose to close the connection if the
+ * 	handshake fails.  The connection will be trashed if the error is
+ * 	unrecoverable.
+ */
+static int
+pop_auth (server, username, host, flags)
+     popserver server;
+     char *username, *host;
+     int flags;
+{
+  int gss_flags, ret;
+  char *fromserver;
+  OM_uint32 max_size, t_flags;
+  gss_ctx_id_t gss_context = GSS_C_NO_CONTEXT;
+  gss_buffer_desc in_tok, out_tok;
+  gss_name_t svc_name;
+  OM_uint32 major, minor, t_minor;
+
+  /* calculate usable protection mechanisms */
+  gss_flags = (GSSAPI_PROTECTION &
+	       ~(((flags & POP_NO_NOPROT) ? GSSAPI_NOPROT : 0) |
+		 ((flags & POP_NO_INTEG) ? GSSAPI_INTEGRITY : 0) |
+		 ((flags & POP_NO_ENCRYPT) ? GSSAPI_PRIVACY : 0)));
+
+  if (gss_flags == 0)
+    {
+      strcpy (pop_error, "Unable to provide selected protection level");
+      return (-1);
+    }
+
+  /* import service name of pop server */
+  in_tok.value = (void *) malloc (strlen (host) + sizeof (GSSAPI_SERVICE) + 2);
+
+  if (! in_tok.value)
+    {
+      strcpy (pop_error, "Out of memory in pop_auth");
+      return (-1);
+    }
+
+  sprintf ((char *) in_tok.value, "%s@%s", GSSAPI_SERVICE, host);
+  in_tok.length = strlen ((char *) in_tok.value);
+
+  {
+    gss_OID_desc svc_name_oid = GSSAPI_SVC_TYPE;
+
+    major = gss_import_name (&minor, &in_tok, &svc_name_oid, &svc_name);
+  }
+
+  free ((char *) in_tok.value);
+
+  if (major != GSS_S_COMPLETE)
+    {
+      gen_gss_error ("parsing name", major, minor);
+      return (-1);
+    }
+
+  /* begin GSSAPI authentication handshake */
+  if (sendline (server, "AUTH GSSAPI") || (pop_getline (server, &fromserver) < 0))
+    {
+      gss_release_name (&t_minor, &svc_name);
+      return (-1);
+    }
+
+  do
+    {
+      /* sanity-check server response */
+      if (strncmp (fromserver, "+ ", 2))
+	{
+	  gss_release_name (&t_minor, &svc_name);
+	  if (gss_context != GSS_C_NO_CONTEXT)
+	    gss_delete_sec_context (&t_minor, &gss_context, GSS_C_NO_BUFFER);
+	  if (0 == strncmp (fromserver, "-ERR", 4))
+	    {
+	      strncpy (pop_error, fromserver, ERROR_MAX);
+	      return (-1);
+	    }
+	  else
+	    {
+	      pop_trash (server);
+	      strcpy (pop_error,
+		      "Unexpected response from POP server in pop_auth");
+	      return (-2);
+	    }
+	}
+
+      if (strlen (fromserver) > 2)
+	{
+	  /* base 64 decode the response... */
+	  ret = b64_decode (fromserver + 2, &in_tok);
+	  if (ret != B64_SUCCESS)
+	    {
+	      gss_release_name (&t_minor, &svc_name);
+	      if (gss_context != GSS_C_NO_CONTEXT)
+		gss_delete_sec_context (&t_minor, &gss_context,
+					GSS_C_NO_BUFFER);
+	      sendline (server, "*");
+	      strcpy (pop_error, b64_error[ret]);
+	      return (-1);
+	    }
+	}
+      else
+	{
+	  in_tok.length = 0;
+	  in_tok.value = 0;
+	}
+
+      /* call init_sec_context */
+      major = gss_init_sec_context (&minor, GSS_C_NO_CREDENTIAL, &gss_context,
+				    svc_name, GSS_C_NULL_OID,
+				    GSS_C_MUTUAL_FLAG, 0,
+				    GSS_C_NO_CHANNEL_BINDINGS,
+				    in_tok.length ? & in_tok : GSS_C_NO_BUFFER,
+				    0, &out_tok, 0, 0);
+
+      if (in_tok.length != 0)
+	free ((char *) in_tok.value);
+
+      /* check for error */
+      if (GSS_ERROR (major))
+	{
+	  gss_release_name (&t_minor, &svc_name);
+	  if (gss_context != GSS_C_NO_CONTEXT)
+	    gss_delete_sec_context (&t_minor, &gss_context, GSS_C_NO_BUFFER);
+	  sendline (server, "*");
+	  gen_gss_error ("in init_sec_context", major, minor);
+	  return (-1);
+	}
+
+      if (out_tok.length != 0)
+	{
+	  /* base 64 encode output token, if any */
+	  ret = b64_encode (&out_tok, &fromserver);
+
+	  gss_release_buffer (&t_minor, &out_tok);
+
+	  if (ret != B64_SUCCESS)
+	    {
+	      gss_release_name (&t_minor, &svc_name);
+	      if (gss_context != GSS_C_NO_CONTEXT)
+		gss_delete_sec_context (&t_minor, &gss_context,
+					GSS_C_NO_BUFFER);
+	      sendline (server, "*");
+	      strcpy (pop_error, b64_error[ret]);
+	      return (-1);
+	    }
+
+	  /* send output token... */
+	  ret = sendline (server, fromserver);
+
+	  free (fromserver);
+	}
+      else
+	/* empty output token... */
+	ret = sendline (server, "");
+
+      /* get next token from server */
+      if (ret || (pop_getline (server, &fromserver) < 0))
+	{
+	  gss_release_name (&t_minor, &svc_name);
+	  if (gss_context != GSS_C_NO_CONTEXT)
+	    gss_delete_sec_context (&t_minor, &gss_context, GSS_C_NO_BUFFER);
+	  return (-1);
+	}
+    } while ((major & GSS_S_CONTINUE_NEEDED));
+
+  /* release name... */
+  gss_release_name (&t_minor, &svc_name);
+
+  /* get final response from server */
+  if (strncmp (fromserver, "+ ", 2))
+    {
+      gss_delete_sec_context (&t_minor, &gss_context, GSS_C_NO_BUFFER);
+      if (0 == strncmp (fromserver, "-ERR", 4))
+	{
+	  strncpy (pop_error, fromserver, ERROR_MAX);
+	  return (-1);
+	}
+      else
+	{
+	  pop_trash (server);
+	  strcpy (pop_error,
+		  "Unexpected response from POP server in pop_auth");
+	  return (-2);
+	}
+    }
+
+  /* base 64 decode... */
+  ret = b64_decode (fromserver + 2, &in_tok);
+  if (ret != B64_SUCCESS)
+    {
+      gss_delete_sec_context (&t_minor, &gss_context, GSS_C_NO_BUFFER);
+      sendline (server, "*");
+      strcpy (pop_error, b64_error[ret]);
+      return (-1);
+    }
+
+  /* unwrap... */
+  major = gss_unwrap (&minor, gss_context, &in_tok, &out_tok, 0, 0);
+
+  free ((char *) in_tok.value);
+
+  if (major != GSS_S_COMPLETE || out_tok.length != sizeof (t_flags))
+    {
+      if (out_tok.length != 0)
+	gss_release_buffer (&t_minor, &out_tok);
+      gss_delete_sec_context (&t_minor, &gss_context, GSS_C_NO_BUFFER);
+      sendline (server, "*");
+      gen_gss_error ("in gss_unwrap", major, minor);
+      return (-1);
+    }
+
+  /* get and check flags/size */
+  bcopy ((void *) out_tok.value, (void *) &t_flags, sizeof (t_flags));
+
+  gss_release_buffer (&t_minor, &out_tok);
+
+  max_size = ntohl (t_flags);
+
+  t_flags = ((max_size & 0xFF000000) >> 24) & gss_flags;
+  max_size &= 0x00FFFFFF;
+
+  if ((t_flags & GSSAPI_PRIVACY))
+    gss_flags = GSSAPI_PRIVACY;
+
+  else if ((t_flags & GSSAPI_INTEGRITY))
+    gss_flags = GSSAPI_INTEGRITY;
+
+  else if ((t_flags & GSSAPI_NOPROT))
+    gss_flags = GSSAPI_NOPROT;
+
+  else
+    {
+      gss_delete_sec_context (&t_minor, &gss_context, GSS_C_NO_BUFFER);
+      sendline (server, "*");
+      strcpy (pop_error, "Server does not provide selected protection level");
+      return (-1);
+    }
+
+  if (max_size == 0)
+    {
+      gss_delete_sec_context (&t_minor, &gss_context, GSS_C_NO_BUFFER);
+      sendline (server, "*");
+      strcpy (pop_error, "Bad server max length");
+      return (-1);
+    }
+
+  if ((gss_flags & GSSAPI_NEEDWRAP))
+    {
+      major = gss_wrap_size_limit (&t_minor, gss_context,
+				   (gss_flags & GSSAPI_PRIVACY) ? 1 : 0,
+				   GSS_C_QOP_DEFAULT,
+				   (max_size < GSSAPI_RCVBUF) ? max_size :
+				   GSSAPI_RCVBUF, &max_size);
+      if (major != GSS_S_COMPLETE)
+	{
+	  gss_delete_sec_context (&t_minor, &gss_context, GSS_C_NO_BUFFER);
+	  sendline (server, "*");
+	  gen_gss_error ("getting max size", major, minor);
+	  return (-1);
+	}
+    }
+
+  /* generate return flags */
+  {
+    OM_uint32 tmp;
+
+    tmp = (((gss_flags << 24) & 0xFF000000) | (GSSAPI_RCVBUF & 0x00FFFFFF));
+    t_flags = ntohl (tmp);
+  }
+
+  in_tok.length = sizeof (t_flags) + strlen (username);
+  in_tok.value = (void *) malloc (in_tok.length);
+
+  if (! in_tok.value)
+    {
+      gss_delete_sec_context (&t_minor, &gss_context, GSS_C_NO_BUFFER);
+      sendline (server, "*");
+      strcpy (pop_error, "Out of memory in pop_auth");
+      return (-1);
+    }
+
+  bcopy ((void *) &t_flags, in_tok.value, sizeof (t_flags));
+  bcopy ((void *) username,
+	 (void *) (((char *) in_tok.value) + sizeof (t_flags)),
+	 in_tok.length - sizeof (t_flags));
+
+  /* wrap result */
+  major = gss_wrap (&minor, gss_context, 0, GSS_C_QOP_DEFAULT,
+		    &in_tok, 0, &out_tok);
+
+  free ((char *) in_tok.value);
+
+  if (major != GSS_S_COMPLETE || out_tok.length == 0)
+    {
+      if (out_tok.length != 0)
+	gss_release_buffer (&t_minor, &out_tok);
+      gss_delete_sec_context (&t_minor, &gss_context, GSS_C_NO_BUFFER);
+      sendline (server, "*");
+      gen_gss_error ("in gss_wrap", major, minor);
+      return (-1);
+    }
+
+  /* base 64 encode... */
+  ret = b64_encode (&out_tok, &fromserver);
+
+  gss_release_buffer (&t_minor, &out_tok);
+
+  if (ret != B64_SUCCESS)
+    {
+      gss_delete_sec_context (&t_minor, &gss_context, GSS_C_NO_BUFFER);
+      sendline (server, "*");
+      strcpy (pop_error, b64_error[ret]);
+      return (-1);
+    }
+
+  /* send to server */
+  ret = sendline (server, fromserver);
+
+  free (fromserver);
+
+  /* see if the server likes me... */
+  if (ret || getok (server))
+    {
+      gss_delete_sec_context (&t_minor, &gss_context, GSS_C_NO_BUFFER);
+      return (-1);
+    }
+
+  /* stash context */
+  {
+    struct _pop_gssapi *gss_data;
+
+    gss_data = (struct _pop_gssapi *) malloc (sizeof (struct _pop_gssapi));
+
+    if (! gss_data)
+      {
+	pop_trash (server);
+	gss_delete_sec_context (&t_minor, &gss_context, GSS_C_NO_BUFFER);
+	strcpy (pop_error, "Out of memory in pop_auth");
+	return (-2);
+      }
+
+    gss_data->gss_flags = gss_flags;
+    gss_data->max_size = max_size;
+    gss_data->gss_context = gss_context;
+
+    server->extra = gss_data;
+  }
+
+  return (0);
+}
+
+/*
+ * Add as much error text to pop_error as will fit, but only put complete
+ * messages
+ */
+static void
+gen_gss_error (msg, major, minor)
+     char *msg;
+     OM_uint32 major, minor;
+{
+  char *p = pop_error, *t, *saved;
+  int max = ERROR_MAX - 1; /* for \0 */
+  OM_uint32 t_minor, msg_ctx = 0;
+  gss_buffer_desc gss_msg;
+
+  while (*msg && max)
+    {
+      *p++ = *msg++;
+      max--;
+    }
+
+  if (max >= 2)
+    {
+      saved = p;
+      *p++ = ':';
+      *p++ = ' ';
+      max -= 2;
+    }
+  else
+    {
+      *p = '\0';
+      return;
+    }
+
+  do
+    {
+      gss_display_status (&t_minor, major, GSS_C_GSS_CODE, GSS_C_NO_OID,
+			  &msg_ctx, &gss_msg);
+      for (t = (char *) gss_msg.value; *t && max; max--)
+	{
+	  *p++ = *t++;
+	}
+      gss_release_buffer (&t_minor, &gss_msg);
+      if (max == 0)
+	{
+	  *saved = '\0';
+	  return;
+	}
+    } while (msg_ctx);
+
+  saved = p;
+
+  do
+    {
+      gss_display_status (&t_minor, minor, GSS_C_MECH_CODE, GSS_C_NO_OID,
+			  &msg_ctx, &gss_msg);
+      for (t = (char *) gss_msg.value; *t && max; max--)
+	{
+	  *p++ = *t++;
+	}
+      gss_release_buffer (&t_minor, &gss_msg);
+      if (max == 0)
+	{
+	  *saved = '\0';
+	  return;
+	}
+    } while (msg_ctx);
+
+  *p = '\0';
+  return;
+}
+
+/*
+ * table-based base64 decoding function; takes 4 characters from in and
+ * writes from 1 to 3 bytes to out, storing the amount written in len
+ */
+static int
+b64_d (in, out, len)
+     char *in, *out;
+     int *len;
+{
+  int decodearray[] =
+  {
+    0x3e,   -1,   -1,   -1, 0x3f, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a,
+    0x3b, 0x3c, 0x3d,   -1,   -1,   -1,   -1,   -1,   -1,   -1, 0x00, 0x01,
+    0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
+    0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
+      -1,   -1,   -1,   -1,   -1,   -1, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
+    0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33
+  };
+
+  int d;
+
+  if (!in || !out || !len)
+    return (B64_BADPARAM);
+
+  if (*in < '+' || *in > 'z')
+    return (B64_BADCHAR);
+
+  d = decodearray[*(in++) - '+'];
+  if (d == -1)
+    return (B64_BADCHAR);
+  *out = d << 2;
+
+  if (*in < '+' || *in > 'z')
+    return (B64_BADCHAR);
+
+  d = decodearray[*(in++) - '+'];
+  if (d == -1)
+    return (B64_BADCHAR);
+  *(out++) |= d >> 4;
+  *out = (d & 15) << 4;
+
+  if (*in < '+' || *in > 'z')
+    return (B64_BADCHAR);
+  else if (*in == '=')
+    if (*(in + 1) != '=')
+      return (B64_BADPAD);
+    else
+      {
+	*len = 1;
+	return (B64_SUCCESS);
+      }
+
+  d = decodearray[*(in++) - '+'];
+  if (d == -1)
+    return (B64_BADCHAR);
+  *(out++) |= d >> 2;
+  *out = (d & 3) << 6;
+
+  if (*in < '+' || *in > 'z')
+    return (B64_BADCHAR);
+  else if (*in == '=')
+    {
+      *len = 2;
+      return (B64_SUCCESS);
+    }
+
+  d = decodearray[*in - '+'];
+  if (d == -1)
+    return (B64_BADCHAR);
+  *out |= d;
+
+  *len = 3;
+  return (B64_SUCCESS);
+}
+
+/*
+ * simple base64 encoding function that takes from 0 to 3 bytes and
+ * outputs 4 encoded characters, with appropriate padding
+ */
+static int
+b64_e (in, out, len)
+     unsigned char *in, *out;
+     int len;
+{
+  unsigned char codearray[] =
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+  if (!in || !out || len <= 0 || len > 3)
+    return (B64_BADPARAM);
+
+  *(out++) = codearray[((*in) >> 2)];
+
+  if (--len == 0)
+    {
+      *(out++) = codearray[(((*in) & 3) << 4)];
+      *(out++) = '=';
+      *out = '=';
+      return (B64_SUCCESS);
+    }
+
+  *(out++) = codearray[(((*in) & 3) << 4) | ((*(in + 1)) >> 4)];
+  in++;
+
+  if (--len == 0)
+    {
+      *(out++) = codearray[(((*in) & 15) << 2)];
+      *out = '=';
+      return (B64_SUCCESS);
+    }
+
+  *(out++) = codearray[(((*in) & 15) << 2) | ((*(in + 1)) >> 6)];
+  *out = codearray[((*(in + 1)) & 63)];
+
+  return (B64_SUCCESS);
+}
+
+/*
+ * given an input string, generate an output gss_buffer_t containing the
+ * decoded data and correct length; works by repeatedly driving b64_d ()
+ * over the input string
+ */
+static int
+b64_decode (enc, dec)
+     char *enc;
+     gss_buffer_t dec;
+{
+  char *tmp;
+  int inlen, outlen = 0, t_len, ret;
+
+  if (!enc || !dec)
+    return (B64_BADPARAM);
+
+  dec->value = 0;
+  dec->length = 0;
+
+  inlen = strlen (enc);
+  if ((inlen % 4))
+    return (B64_BADLEN);
+
+  dec->value = (void *) (tmp = (char *) malloc ((inlen / 4) * 3));
+
+  if (! tmp)
+    return (B64_NOMEM);
+
+  for (; inlen; inlen -= 4)
+    {
+      ret = b64_d (enc, tmp, &t_len);
+      if (ret != B64_SUCCESS)
+	{
+	  free ((char *) dec->value);
+	  dec->value = 0;
+	  return (ret);
+	}
+      else if (t_len != 3)
+	{
+	  dec->length = outlen + t_len;
+	  return (B64_SUCCESS);
+	}
+      else
+	{
+	  enc += 4;
+	  tmp += t_len;
+	  outlen += t_len;
+	}
+    }
+
+  dec->length = outlen;
+  return (B64_SUCCESS);
+}
+
+/*
+ * given a gss_buffer_t, generate an encoded string containing the data.
+ * works by repeatedly driving b64_e () over the contents of the buffer_t
+ */
+static int
+b64_encode (dec, enc)
+     gss_buffer_t dec;
+     char **enc;
+{
+  unsigned char *tmp, *in;
+  int ret, len;
+
+  if (!dec || !enc)
+    return (B64_BADPARAM);
+
+  in = (unsigned char *) dec->value;
+  len = dec->length;
+  *enc = (char *) (tmp = (unsigned char *) malloc (((len * 4) / 3) + 5));
+
+  if (! tmp)
+    return (B64_NOMEM);
+
+  do
+    {
+      ret = b64_e (in, tmp, len >= 3 ? 3 : len);
+      if (ret != B64_SUCCESS)
+	{
+	  free (*enc);
+	  *enc = 0;
+	  return (ret);
+	}
+      else
+	{
+	  in += 3;
+	  tmp += 4;
+	}
+    } while ((len -= 3) > 0);
+
+  *tmp = '\0';
+
+  return (B64_SUCCESS);
+}
+
+#endif /* GSSAPI */
+
 /* Return a pointer to the first CRLF in IN_STRING, which can contain
    embedded nulls and has LEN characters in it not including the final
    null, or 0 if it does not contain one.  */
--- a/lib-src/pop.h	Mon Apr 06 22:10:30 1998 +0000
+++ b/lib-src/pop.h	Mon Apr 06 22:16:30 1998 +0000
@@ -36,6 +36,7 @@
   int buffer_size, buffer_index;
   int in_multi;
   int trash_started;
+  void *extra;
 };
 
 typedef struct _popserver *popserver;
@@ -47,6 +48,30 @@
 #define POP_NO_KERBEROS	(1<<0)
 #define POP_NO_HESIOD	(1<<1)
 #define POP_NO_GETPASS 	(1<<2)
+#define POP_NO_GSSAPI	(1<<3)	/* don't use the GSSAPI */
+#define POP_NO_NOPROT	(1<<4)	/* prohibit no protection; this *only* */
+				/* makes sense if you use GSSAPI */
+#define POP_NO_INTEG	(1<<5)	/* don't use plain integrity */
+#define POP_NO_ENCRYPT	(1<<6)	/* don't use encryption */
+
+/*
+ * GSSAPI documentation
+ *
+ * This version will attempt to perform a GSSAPI handshake first; if this
+ * fails, then it will attempt standard POP authentication.  Note that
+ * library conflicts may prevent the use of this with the Kerberos
+ * kpop hack.
+ *
+ * If you specify POP_NO_NOPROT and this library is unable to provide either
+ * integrity protection or encryption, pop_open() will fail.  The pop_open()
+ * call will attempt the highest level protection available; i.e., if both
+ * server and client support encryption (and you do not provide the
+ * POP_NO_ENCRYPT flag), that will be used; if both server and client support
+ * integrity protection (and you do not provide the POP_NO_INTEG flag), that
+ * will be used.  If neither of these are available, and you have not
+ * specified the POP_NO_NOPROT flag, then this will be a normal, unprotected
+ * connection.
+ */
 
 #ifdef __STDC__
 #define _ARGS(a) a