]> arthur.barton.de Git - netatalk.git/commitdiff
remove \r lineendings
authorbfernhomberg <bfernhomberg>
Sun, 20 Jun 2004 15:51:46 +0000 (15:51 +0000)
committerbfernhomberg <bfernhomberg>
Sun, 20 Jun 2004 15:51:46 +0000 (15:51 +0000)
etc/uams/uams_gss.c

index 0a75306090268002da112c5f76cc8de0d5af1b90..05a1cfbe35822a796f508788ad63726008d9c2f3 100644 (file)
-/*\r
- * $Id: uams_gss.c,v 1.2.2.3 2004-06-15 00:35:06 bfernhomberg Exp $\r
- *\r
- * Copyright (c) 1990,1993 Regents of The University of Michigan.\r
- * Copyright (c) 1999 Adrian Sun (asun@u.washington.edu) \r
- * Copyright (c) 2003 The Reed Institute\r
- * All Rights Reserved.  See COPYRIGHT.\r
- */\r
-\r
-#ifdef HAVE_CONFIG_H\r
-#include "config.h"\r
-#endif /* HAVE_CONFIG_H */\r
-\r
-#ifndef ATACC\r
-#include <stdio.h>\r
-#include <stdlib.h>\r
-#ifdef HAVE_UNISTD_H\r
-#include <unistd.h>\r
-#endif /* HAVE_UNISTD_H */\r
-\r
-/* STDC check */\r
-#if STDC_HEADERS\r
-#include <string.h>\r
-#else /* STDC_HEADERS */\r
-#ifndef HAVE_STRCHR\r
-#define strchr index\r
-#define strrchr index\r
-#endif /* HAVE_STRCHR */\r
-char *strchr (), *strrchr ();\r
-#ifndef HAVE_MEMCPY\r
-#define memcpy(d,s,n) bcopy ((s), (d), (n))\r
-#define memmove(d,s,n) bcopy ((s), (d), (n))\r
-#endif /* ! HAVE_MEMCPY */\r
-#endif /* STDC_HEADERS */\r
-\r
-#include <errno.h>\r
-#include <atalk/logger.h>\r
-#include <atalk/afp.h>\r
-#include <atalk/uam.h>\r
-\r
-/* Kerberos includes */\r
-\r
-#if HAVE_GSSAPI_H\r
-#include <gssapi.h>\r
-#endif\r
-\r
-#if HAVE_GSSAPI_GSSAPI_H\r
-#include <gssapi/gssapi.h>\r
-#endif\r
-\r
-#if HAVE_GSSAPI_GSSAPI_GENERIC_H\r
-#include <gssapi/gssapi_generic.h>\r
-#endif\r
-\r
-#if HAVE_GSSAPI_GSSAPI_KRB5_H\r
-#include <gssapi/gssapi_krb5.h>\r
-#endif\r
-\r
-#if HAVE_COM_ERR_H\r
-#include <com_err.h>\r
-#endif\r
-\r
-/* We work around something I don't entirely understand... */\r
-/* BF: This is a Heimdal/MIT compatibility fix */\r
-#ifndef HAVE_GSS_C_NT_HOSTBASED_SERVICE\r
-#define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name\r
-#endif\r
-\r
-#define MIN(a, b) ((a > b) ? b : a)\r
-\r
-static void log_status( char *s, OM_uint32 major_status, \r
-                       OM_uint32 minor_status )\r
-{\r
-    gss_buffer_desc msg = GSS_C_EMPTY_BUFFER;\r
-    OM_uint32 min_status, maj_status;\r
-    OM_uint32 maj_ctx = 0, min_ctx = 0;\r
-\r
-    while (1) {\r
-       maj_status = gss_display_status( &min_status, major_status,\r
-                                       GSS_C_GSS_CODE, GSS_C_NULL_OID,\r
-                                       &maj_ctx, &msg );\r
-        LOG(log_info, logtype_uams, "uams_gss.c :do_gss_auth: %s %.*s (error %s)", s,\r
-                               (int)msg.length, msg.value, strerror(errno));\r
-       gss_release_buffer(&min_status, &msg);\r
-\r
-       if (!maj_ctx)\r
-           break;\r
-    }\r
-    while (1) {\r
-       maj_status = gss_display_status( &min_status, minor_status,\r
-                                       GSS_C_MECH_CODE, GSS_C_NULL_OID, // gss_mech_krb5,\r
-                                       &min_ctx, &msg );\r
-        LOG(log_info, logtype_uams, "uams_gss.c :do_gss_auth: %s %.*s (error %s)", s, \r
-                               (int)msg.length, msg.value, strerror(errno));\r
-       gss_release_buffer(&min_status, &msg);\r
-\r
-       if (!min_ctx)\r
-           break;\r
-    }\r
-}\r
-\r
-\r
-void log_ctx_flags( OM_uint32 flags )\r
-{\r
-    if (flags & GSS_C_DELEG_FLAG)\r
-        LOG(log_info, logtype_uams, "uams_gss.c :context flag: GSS_C_DELEG_FLAG" );\r
-    if (flags & GSS_C_MUTUAL_FLAG)\r
-        LOG(log_info, logtype_uams, "uams_gss.c :context flag: GSS_C_MUTUAL_FLAG" );\r
-    if (flags & GSS_C_REPLAY_FLAG)\r
-        LOG(log_info, logtype_uams, "uams_gss.c :context flag: GSS_C_REPLAY_FLAG" );\r
-    if (flags & GSS_C_SEQUENCE_FLAG)\r
-        LOG(log_info, logtype_uams, "uams_gss.c :context flag: GSS_C_SEQUENCE_FLAG" );\r
-    if (flags & GSS_C_CONF_FLAG)\r
-        LOG(log_info, logtype_uams, "uams_gss.c :context flag: GSS_C_CONF_FLAG" );\r
-    if (flags & GSS_C_INTEG_FLAG)\r
-        LOG(log_info, logtype_uams, "uams_gss.c :context flag: GSS_C_INTEG_FLAG" );\r
-}\r
-\r
-/* return 0 on success */\r
-static int do_gss_auth( char *service, char *ibuf, int ticket_len,\r
-                char *rbuf, int *rbuflen, char *username, int ulen,\r
-                struct session_info *sinfo ) \r
-{\r
-    OM_uint32 major_status = 0, minor_status = 0;\r
-    gss_name_t server_name;\r
-    gss_cred_id_t server_creds;\r
-    gss_ctx_id_t context_handle = GSS_C_NO_CONTEXT;\r
-    gss_buffer_desc ticket_buffer, authenticator_buff, sesskey_buff, wrap_buff;\r
-    gss_name_t client_name;\r
-    OM_uint32  ret_flags;\r
-    int ret = 0;\r
-    /* FIXME\r
-     * princ specifies the principal to be used.\r
-     * The user specifies it when configuring afpd anyway,\r
-     * we should be able to detect it.\r
-     */\r
-    gss_buffer_desc s_princ_buffer;\r
-\r
-    s_princ_buffer.value = service;\r
-    s_princ_buffer.length = strlen( service ) + 1;\r
-    \r
-    /* outline:\r
-     * gss_import_name (need a way to get this from afpd)\r
-     * gss_acquire_credential\r
-     * gss_accept_sec_context\r
-     * ...\r
-     */\r
-    LOG(log_debug, logtype_uams, "uams_gss.c :do_gss_auth: importing name" );\r
-    major_status = gss_import_name( &minor_status, \r
-                   &s_princ_buffer, \r
-                   GSS_C_NT_HOSTBASED_SERVICE,\r
-                   &server_name );\r
-    if (major_status != GSS_S_COMPLETE) {\r
-       log_status( "import_name", major_status, minor_status );\r
-       ret = 1;\r
-       goto cleanup_vars;\r
-    }\r
-    \r
-    LOG(log_debug, logtype_uams, \r
-       "uams_gss.c :do_gss_auth: acquiring credentials (uid = %d, keytab = %s)",\r
-        (int)geteuid(), getenv( "KRB5_KTNAME") );\r
-\r
-    major_status = gss_acquire_cred( &minor_status, server_name, \r
-                       GSS_C_INDEFINITE, GSS_C_NO_OID_SET, GSS_C_ACCEPT,\r
-                       &server_creds, NULL, NULL );    \r
-    if (major_status != GSS_S_COMPLETE) {\r
-       log_status( "acquire_cred", major_status, minor_status );\r
-       ret = 1;\r
-       goto cleanup_vars;\r
-    }\r
-\r
-    /* The GSSAPI docs say that this should be done in a big "do" loop,\r
-     * but Apple's implementation doesn't seem to support this behavior.\r
-     */\r
-    ticket_buffer.length = ticket_len;\r
-    ticket_buffer.value = ibuf;\r
-    authenticator_buff.length = 0;\r
-    authenticator_buff.value = NULL;\r
-    LOG(log_debug, logtype_uams, "uams_gss.c :do_gss_auth: accepting context (ticketlen: %u, value: %X)", ticket_buffer.length, ticket_buffer.value );\r
-    major_status = gss_accept_sec_context( &minor_status, &context_handle,\r
-                       server_creds, &ticket_buffer, GSS_C_NO_CHANNEL_BINDINGS,\r
-                       &client_name, NULL, &authenticator_buff,\r
-                       &ret_flags, NULL, NULL );\r
-\r
-    if (major_status == GSS_S_COMPLETE) {\r
-       gss_buffer_desc client_name_buffer;\r
-\r
-#ifdef DEBUG1\r
-       log_ctx_flags( ret_flags );\r
-#endif\r
-       /* use gss_display_name on client_name */\r
-       major_status = gss_display_name( &minor_status, client_name,\r
-                               &client_name_buffer, (gss_OID *)NULL );\r
-       if (major_status == GSS_S_COMPLETE) {\r
-\r
-           sesskey_buff.value = sinfo->sessionkey;\r
-           sesskey_buff.length = sinfo->sessionkey_len;\r
-\r
-           gss_wrap (&minor_status, context_handle, 1, GSS_C_QOP_DEFAULT, &sesskey_buff, NULL, &wrap_buff); \r
-           if ( minor_status == GSS_S_COMPLETE) {\r
-               sinfo->cryptedkey = malloc ( wrap_buff.length );\r
-               memcpy (sinfo->cryptedkey, wrap_buff.value, wrap_buff.length);\r
-               sinfo->cryptedkey_len = wrap_buff.length;\r
-           }\r
-           else {\r
-               log_status( "GSS wrap", major_status, minor_status );\r
-           }\r
-\r
-           if (wrap_buff.value)\r
-               gss_release_buffer( &minor_status, &wrap_buff );\r
-\r
-           u_int16_t auth_len = htons( authenticator_buff.length );\r
-           /* save the username... note that doing it this way is\r
-            * not the best idea: if a principal is truncated, a user could be\r
-            * impersonated\r
-            */\r
-           memcpy( username, client_name_buffer.value, \r
-               MIN(client_name_buffer.length, ulen - 1));\r
-           username[MIN(client_name_buffer.length, ulen - 1)] = 0;\r
-       \r
-            LOG(log_debug, logtype_uams, "uams_gss.c :do_gss_auth: user is %s!", username );\r
-           /* copy the authenticator length into the reply buffer */\r
-           memcpy( rbuf, &auth_len, sizeof(auth_len) );\r
-           *rbuflen += sizeof(auth_len), rbuf += sizeof(auth_len);\r
-\r
-           /* copy the authenticator value into the reply buffer */\r
-           memcpy( rbuf, authenticator_buff.value, authenticator_buff.length );\r
-           *rbuflen += authenticator_buff.length;\r
-\r
-           gss_release_buffer( &minor_status, &client_name_buffer );\r
-       } else {\r
-           log_status( "display_name", major_status, minor_status );\r
-           ret = 1;\r
-       }\r
-\r
-       \r
-\r
-\r
-       /* Clean up after ourselves */\r
-        gss_release_name( &minor_status, &client_name );\r
-\r
-       /* This will SIGSEGV, as it would free ibuf */\r
-        /*gss_release_buffer( &minor_status, &ticket_buffer );*/\r
-\r
-       if ( authenticator_buff.value)\r
-               gss_release_buffer( &minor_status, &authenticator_buff );\r
-\r
-        gss_delete_sec_context( &minor_status, \r
-                       &context_handle, NULL );\r
-    } else {\r
-       log_status( "accept_sec_context", major_status, minor_status );\r
-        ret = 1;\r
-    }\r
-    gss_release_cred( &minor_status, &server_creds );\r
-\r
-cleanup_vars:\r
-    gss_release_name( &minor_status, &server_name );\r
-    \r
-    return ret;\r
-}\r
-\r
-/* -------------------------- */\r
-static int gss_login(void *obj, struct passwd **uam_pwd,\r
-                    char *ibuf, int ibuflen,\r
-                    char *rbuf, int *rbuflen)\r
-{\r
-\r
-    u_int16_t  temp16;\r
-\r
-    *rbuflen = 0;\r
-\r
-    /* The reply contains a two-byte ID value - note \r
-     * that Apple's implementation seems to always return 1 as well\r
-     */\r
-    temp16 = htons( 1 );\r
-    memcpy(rbuf, &temp16, sizeof(temp16));\r
-    *rbuflen += sizeof(temp16);\r
-    return AFPERR_AUTHCONT;\r
-}\r
-\r
-static int gss_logincont(void *obj, struct passwd **uam_pwd,\r
-                     char *ibuf, int ibuflen,\r
-                     char *rbuf, int *rbuflen)\r
-{\r
-    struct passwd *pwd = NULL;\r
-    u_int16_t login_id;\r
-    char *username;\r
-    u_int16_t ticket_len;\r
-    char *p;\r
-    int rblen;\r
-    char *service;\r
-    int userlen, servicelen;\r
-    struct session_info *sinfo;\r
-\r
-    /* Apple's AFP 3.1 documentation specifies that this command\r
-     * takes the following format:\r
-     * pad (byte)\r
-     * id returned in LoginExt response (u_int16_t)\r
-     * username (format unspecified) padded, when necessary, to end on an even boundary\r
-     * ticket length (u_int16_t)\r
-     * ticket\r
-     */\r
-\r
-    /* Observation of AFP clients in the wild indicate that the actual\r
-     * format of this request is as follows:\r
-     * pad (byte) [consumed before login_ext is called]\r
-     * ?? (byte) - always observed to be 0\r
-     * id returned in LoginExt response (u_int16_t)\r
-     * username, encoding unspecified, null terminated C string, \r
-     *   padded when the terminating null is an even numbered byte.\r
-     *   The packet is formated such that the username begins on an \r
-     *   odd numbered byte. Eg if the username is 3 characters and the\r
-     *   terminating null makes 4, expect to pad the the result.\r
-     *   The encoding of this string is unknown.\r
-     * ticket length (u_int16_t)\r
-     * ticket\r
-     */\r
-\r
-    rblen = *rbuflen = 0;\r
-\r
-    ibuf++, ibuflen--; /* ?? */\r
-\r
-    /* 2 byte ID from LoginExt -- always '00 01' in this implementation */\r
-    memcpy( &login_id, ibuf, sizeof(login_id) );\r
-    ibuf += sizeof(login_id), ibuflen -= sizeof(login_id);\r
-    login_id = ntohs( login_id );\r
-\r
-    if (uam_afpserver_option(obj, UAM_OPTION_USERNAME, (void *) &username, &userlen) < 0)\r
-        return AFPERR_MISC;\r
-\r
-    if (uam_afpserver_option(obj, UAM_OPTION_KRB5SERVICE, (void *)&service, &servicelen) < 0)\r
-       return AFPERR_MISC;\r
-\r
-    if (service == NULL) \r
-       return AFPERR_MISC;\r
-\r
-    if (uam_afpserver_option(obj, UAM_OPTION_SESSIONINFO, (void *)&sinfo, NULL) < 0)\r
-       return AFPERR_MISC;\r
-\r
-    if (sinfo->sessionkey == NULL || sinfo->sessionkey_len == 0) {\r
-        LOG(log_info, logtype_uams, "internal error: sessionkey not set");\r
-        return AFPERR_PARAM;\r
-    }\r
-\r
-    /* We skip past the 'username' parameter because all that matters is the ticket */\r
-    p = ibuf;\r
-    while( *ibuf && ibuflen ) { ibuf++, ibuflen--; }\r
-    if (ibuflen < 4) {\r
-        LOG(log_info, logtype_uams, "uams_gss.c :LoginCont: user is %s, no ticket", p);\r
-       return AFPERR_PARAM;\r
-    }\r
-\r
-    ibuf++, ibuflen--; /* null termination */\r
-\r
-    if ((ibuf - p + 1) % 2) ibuf++, ibuflen--; /* deal with potential padding */\r
-\r
-    LOG(log_info, logtype_uams, "uams_gss.c :LoginCont: client thinks user is %s", p);\r
-\r
-    memcpy(&ticket_len, ibuf, sizeof(ticket_len));\r
-    ibuf += sizeof(ticket_len); ibuflen -= sizeof(ticket_len);\r
-    ticket_len = ntohs( ticket_len );\r
-\r
-    if (ticket_len > ibuflen) {\r
-        LOG(log_info, logtype_uams, "uams_gss.c :LoginCont: invalid ticket length");\r
-       return AFPERR_PARAM;\r
-    }\r
-\r
-    if (!do_gss_auth(service, ibuf, ticket_len, rbuf, &rblen, username, userlen, sinfo)) {\r
-       char *at = strchr( username, '@' );\r
-\r
-       // Chop off the realm name\r
-       if (at)\r
-           *at = '\0';\r
-       if((pwd = uam_getname( obj, username, userlen )) == NULL) {\r
-           LOG(log_info, logtype_uams, "uam_getname() failed for %s", username);\r
-           return AFPERR_PARAM;\r
-       }\r
-       if (uam_checkuser(pwd) < 0) {\r
-           LOG(log_info, logtype_uams, "%s not a valid user", username);\r
-           return AFPERR_NOTAUTH;\r
-       }\r
-       *rbuflen = rblen;\r
-       *uam_pwd = pwd;\r
-       return AFP_OK;\r
-    } else {\r
-       LOG(log_info, logtype_uams, "do_gss_auth failed" );\r
-       *rbuflen = 0;\r
-        return AFPERR_MISC;\r
-    }\r
-}\r
-\r
-/*\r
- * For the krb5 uam, this function only needs to return a two-byte\r
- * login-session id. None of the data provided by the client up to this\r
- * point is trustworthy as we'll have a signed ticket to parse in logincont.\r
- */\r
-static int gss_login_ext(void *obj, char *uname, struct passwd **uam_pwd,\r
-                    char *ibuf, int ibuflen,\r
-                    char *rbuf, int *rbuflen)\r
-{\r
-    u_int16_t  temp16;\r
-\r
-    *rbuflen = 0;\r
-\r
-    /* The reply contains a two-byte ID value - note \r
-     * that Apple's implementation seems to always return 1 as well\r
-     */\r
-    temp16 = htons( 1 );\r
-    memcpy(rbuf, &temp16, sizeof(temp16));\r
-    *rbuflen += sizeof(temp16);\r
-    return AFPERR_AUTHCONT;\r
-}\r
-\r
-/* logout */\r
-static void gss_logout() {\r
-}\r
-\r
-int uam_setup(const char *path)\r
-{\r
-    if (uam_register(UAM_SERVER_LOGIN_EXT, path, "Client Krb v2", \r
-                  gss_login, gss_logincont, gss_logout, gss_login_ext) < 0)\r
-    if (uam_register(UAM_SERVER_LOGIN, path, "Client Krb v2", \r
-                  gss_login, gss_logincont, gss_logout) < 0)\r
-        return -1;\r
-\r
-  return 0;\r
-}\r
-\r
-static void uam_cleanup(void)\r
-{\r
-  uam_unregister(UAM_SERVER_LOGIN_EXT, "Client Krb v2");\r
-}\r
-\r
-UAM_MODULE_EXPORT struct uam_export uams_gss = {\r
-  UAM_MODULE_SERVER,\r
-  UAM_MODULE_VERSION,\r
-  uam_setup, uam_cleanup\r
-};\r
-#endif\r
+/*
+ * $Id: uams_gss.c,v 1.2.2.4 2004-06-20 15:51:46 bfernhomberg Exp $
+ *
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * Copyright (c) 1999 Adrian Sun (asun@u.washington.edu) 
+ * Copyright (c) 2003 The Reed Institute
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#ifndef ATACC
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+
+/* STDC check */
+#if STDC_HEADERS
+#include <string.h>
+#else /* STDC_HEADERS */
+#ifndef HAVE_STRCHR
+#define strchr index
+#define strrchr index
+#endif /* HAVE_STRCHR */
+char *strchr (), *strrchr ();
+#ifndef HAVE_MEMCPY
+#define memcpy(d,s,n) bcopy ((s), (d), (n))
+#define memmove(d,s,n) bcopy ((s), (d), (n))
+#endif /* ! HAVE_MEMCPY */
+#endif /* STDC_HEADERS */
+
+#include <errno.h>
+#include <atalk/logger.h>
+#include <atalk/afp.h>
+#include <atalk/uam.h>
+
+/* Kerberos includes */
+
+#if HAVE_GSSAPI_H
+#include <gssapi.h>
+#endif
+
+#if HAVE_GSSAPI_GSSAPI_H
+#include <gssapi/gssapi.h>
+#endif
+
+#if HAVE_GSSAPI_GSSAPI_GENERIC_H
+#include <gssapi/gssapi_generic.h>
+#endif
+
+#if HAVE_GSSAPI_GSSAPI_KRB5_H
+#include <gssapi/gssapi_krb5.h>
+#endif
+
+#if HAVE_COM_ERR_H
+#include <com_err.h>
+#endif
+
+/* We work around something I don't entirely understand... */
+/* BF: This is a Heimdal/MIT compatibility fix */
+#ifndef HAVE_GSS_C_NT_HOSTBASED_SERVICE
+#define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name
+#endif
+
+#define MIN(a, b) ((a > b) ? b : a)
+
+static void log_status( char *s, OM_uint32 major_status, 
+                       OM_uint32 minor_status )
+{
+    gss_buffer_desc msg = GSS_C_EMPTY_BUFFER;
+    OM_uint32 min_status, maj_status;
+    OM_uint32 maj_ctx = 0, min_ctx = 0;
+
+    while (1) {
+       maj_status = gss_display_status( &min_status, major_status,
+                                       GSS_C_GSS_CODE, GSS_C_NULL_OID,
+                                       &maj_ctx, &msg );
+        LOG(log_info, logtype_uams, "uams_gss.c :do_gss_auth: %s %.*s (error %s)", s,
+                               (int)msg.length, msg.value, strerror(errno));
+       gss_release_buffer(&min_status, &msg);
+
+       if (!maj_ctx)
+           break;
+    }
+    while (1) {
+       maj_status = gss_display_status( &min_status, minor_status,
+                                       GSS_C_MECH_CODE, GSS_C_NULL_OID, // gss_mech_krb5,
+                                       &min_ctx, &msg );
+        LOG(log_info, logtype_uams, "uams_gss.c :do_gss_auth: %s %.*s (error %s)", s, 
+                               (int)msg.length, msg.value, strerror(errno));
+       gss_release_buffer(&min_status, &msg);
+
+       if (!min_ctx)
+           break;
+    }
+}
+
+
+void log_ctx_flags( OM_uint32 flags )
+{
+    if (flags & GSS_C_DELEG_FLAG)
+        LOG(log_info, logtype_uams, "uams_gss.c :context flag: GSS_C_DELEG_FLAG" );
+    if (flags & GSS_C_MUTUAL_FLAG)
+        LOG(log_info, logtype_uams, "uams_gss.c :context flag: GSS_C_MUTUAL_FLAG" );
+    if (flags & GSS_C_REPLAY_FLAG)
+        LOG(log_info, logtype_uams, "uams_gss.c :context flag: GSS_C_REPLAY_FLAG" );
+    if (flags & GSS_C_SEQUENCE_FLAG)
+        LOG(log_info, logtype_uams, "uams_gss.c :context flag: GSS_C_SEQUENCE_FLAG" );
+    if (flags & GSS_C_CONF_FLAG)
+        LOG(log_info, logtype_uams, "uams_gss.c :context flag: GSS_C_CONF_FLAG" );
+    if (flags & GSS_C_INTEG_FLAG)
+        LOG(log_info, logtype_uams, "uams_gss.c :context flag: GSS_C_INTEG_FLAG" );
+}
+
+/* return 0 on success */
+static int do_gss_auth( char *service, char *ibuf, int ticket_len,
+                char *rbuf, int *rbuflen, char *username, int ulen,
+                struct session_info *sinfo ) 
+{
+    OM_uint32 major_status = 0, minor_status = 0;
+    gss_name_t server_name;
+    gss_cred_id_t server_creds;
+    gss_ctx_id_t context_handle = GSS_C_NO_CONTEXT;
+    gss_buffer_desc ticket_buffer, authenticator_buff, sesskey_buff, wrap_buff;
+    gss_name_t client_name;
+    OM_uint32  ret_flags;
+    int ret = 0;
+    /* FIXME
+     * princ specifies the principal to be used.
+     * The user specifies it when configuring afpd anyway,
+     * we should be able to detect it.
+     */
+    gss_buffer_desc s_princ_buffer;
+
+    s_princ_buffer.value = service;
+    s_princ_buffer.length = strlen( service ) + 1;
+    
+    /* outline:
+     * gss_import_name (need a way to get this from afpd)
+     * gss_acquire_credential
+     * gss_accept_sec_context
+     * ...
+     */
+    LOG(log_debug, logtype_uams, "uams_gss.c :do_gss_auth: importing name" );
+    major_status = gss_import_name( &minor_status, 
+                   &s_princ_buffer, 
+                   GSS_C_NT_HOSTBASED_SERVICE,
+                   &server_name );
+    if (major_status != GSS_S_COMPLETE) {
+       log_status( "import_name", major_status, minor_status );
+       ret = 1;
+       goto cleanup_vars;
+    }
+    
+    LOG(log_debug, logtype_uams, 
+       "uams_gss.c :do_gss_auth: acquiring credentials (uid = %d, keytab = %s)",
+        (int)geteuid(), getenv( "KRB5_KTNAME") );
+
+    major_status = gss_acquire_cred( &minor_status, server_name, 
+                       GSS_C_INDEFINITE, GSS_C_NO_OID_SET, GSS_C_ACCEPT,
+                       &server_creds, NULL, NULL );    
+    if (major_status != GSS_S_COMPLETE) {
+       log_status( "acquire_cred", major_status, minor_status );
+       ret = 1;
+       goto cleanup_vars;
+    }
+
+    /* The GSSAPI docs say that this should be done in a big "do" loop,
+     * but Apple's implementation doesn't seem to support this behavior.
+     */
+    ticket_buffer.length = ticket_len;
+    ticket_buffer.value = ibuf;
+    authenticator_buff.length = 0;
+    authenticator_buff.value = NULL;
+    LOG(log_debug, logtype_uams, "uams_gss.c :do_gss_auth: accepting context (ticketlen: %u)", ticket_buffer.length);
+    major_status = gss_accept_sec_context( &minor_status, &context_handle,
+                       server_creds, &ticket_buffer, GSS_C_NO_CHANNEL_BINDINGS,
+                       &client_name, NULL, &authenticator_buff,
+                       &ret_flags, NULL, NULL );
+
+    if (major_status == GSS_S_COMPLETE) {
+       gss_buffer_desc client_name_buffer;
+
+#ifdef DEBUG1
+       log_ctx_flags( ret_flags );
+#endif
+       /* use gss_display_name on client_name */
+       major_status = gss_display_name( &minor_status, client_name,
+                               &client_name_buffer, (gss_OID *)NULL );
+       if (major_status == GSS_S_COMPLETE) {
+
+           sesskey_buff.value = sinfo->sessionkey;
+           sesskey_buff.length = sinfo->sessionkey_len;
+
+           gss_wrap (&minor_status, context_handle, 1, GSS_C_QOP_DEFAULT, &sesskey_buff, NULL, &wrap_buff); 
+           if ( minor_status == GSS_S_COMPLETE) {
+               sinfo->cryptedkey = malloc ( wrap_buff.length );
+               memcpy (sinfo->cryptedkey, wrap_buff.value, wrap_buff.length);
+               sinfo->cryptedkey_len = wrap_buff.length;
+           }
+           else {
+               log_status( "GSS wrap", major_status, minor_status );
+           }
+
+           if (wrap_buff.value)
+               gss_release_buffer( &minor_status, &wrap_buff );
+
+           u_int16_t auth_len = htons( authenticator_buff.length );
+           /* save the username... note that doing it this way is
+            * not the best idea: if a principal is truncated, a user could be
+            * impersonated
+            */
+           memcpy( username, client_name_buffer.value, 
+               MIN(client_name_buffer.length, ulen - 1));
+           username[MIN(client_name_buffer.length, ulen - 1)] = 0;
+       
+            LOG(log_debug, logtype_uams, "uams_gss.c :do_gss_auth: user is %s!", username );
+           /* copy the authenticator length into the reply buffer */
+           memcpy( rbuf, &auth_len, sizeof(auth_len) );
+           *rbuflen += sizeof(auth_len), rbuf += sizeof(auth_len);
+
+           /* copy the authenticator value into the reply buffer */
+           memcpy( rbuf, authenticator_buff.value, authenticator_buff.length );
+           *rbuflen += authenticator_buff.length;
+
+           gss_release_buffer( &minor_status, &client_name_buffer );
+       } else {
+           log_status( "display_name", major_status, minor_status );
+           ret = 1;
+       }
+
+       
+
+
+       /* Clean up after ourselves */
+        gss_release_name( &minor_status, &client_name );
+
+       /* This will SIGSEGV, as it would free ibuf */
+        /*gss_release_buffer( &minor_status, &ticket_buffer );*/
+
+       if ( authenticator_buff.value)
+               gss_release_buffer( &minor_status, &authenticator_buff );
+
+        gss_delete_sec_context( &minor_status, 
+                       &context_handle, NULL );
+    } else {
+       log_status( "accept_sec_context", major_status, minor_status );
+        ret = 1;
+    }
+    gss_release_cred( &minor_status, &server_creds );
+
+cleanup_vars:
+    gss_release_name( &minor_status, &server_name );
+    
+    return ret;
+}
+
+/* -------------------------- */
+static int gss_login(void *obj, struct passwd **uam_pwd,
+                    char *ibuf, int ibuflen,
+                    char *rbuf, int *rbuflen)
+{
+
+    u_int16_t  temp16;
+
+    *rbuflen = 0;
+
+    /* The reply contains a two-byte ID value - note 
+     * that Apple's implementation seems to always return 1 as well
+     */
+    temp16 = htons( 1 );
+    memcpy(rbuf, &temp16, sizeof(temp16));
+    *rbuflen += sizeof(temp16);
+    return AFPERR_AUTHCONT;
+}
+
+static int gss_logincont(void *obj, struct passwd **uam_pwd,
+                     char *ibuf, int ibuflen,
+                     char *rbuf, int *rbuflen)
+{
+    struct passwd *pwd = NULL;
+    u_int16_t login_id;
+    char *username;
+    u_int16_t ticket_len;
+    char *p;
+    int rblen;
+    char *service;
+    int userlen, servicelen;
+    struct session_info *sinfo;
+
+    /* Apple's AFP 3.1 documentation specifies that this command
+     * takes the following format:
+     * pad (byte)
+     * id returned in LoginExt response (u_int16_t)
+     * username (format unspecified) padded, when necessary, to end on an even boundary
+     * ticket length (u_int16_t)
+     * ticket
+     */
+
+    /* Observation of AFP clients in the wild indicate that the actual
+     * format of this request is as follows:
+     * pad (byte) [consumed before login_ext is called]
+     * ?? (byte) - always observed to be 0
+     * id returned in LoginExt response (u_int16_t)
+     * username, encoding unspecified, null terminated C string, 
+     *   padded when the terminating null is an even numbered byte.
+     *   The packet is formated such that the username begins on an 
+     *   odd numbered byte. Eg if the username is 3 characters and the
+     *   terminating null makes 4, expect to pad the the result.
+     *   The encoding of this string is unknown.
+     * ticket length (u_int16_t)
+     * ticket
+     */
+
+    rblen = *rbuflen = 0;
+
+    if (ibuflen < 3) {
+        LOG(log_info, logtype_uams, "uams_gss.c :LoginCont: received incomplete packet", p);
+       return AFPERR_PARAM;
+    }
+    ibuf++, ibuflen--; /* ?? */
+
+    /* 2 byte ID from LoginExt -- always '00 01' in this implementation */
+    memcpy( &login_id, ibuf, sizeof(login_id) );
+    ibuf += sizeof(login_id), ibuflen -= sizeof(login_id);
+    login_id = ntohs( login_id );
+
+    if (uam_afpserver_option(obj, UAM_OPTION_USERNAME, (void *) &username, &userlen) < 0)
+        return AFPERR_MISC;
+
+    if (uam_afpserver_option(obj, UAM_OPTION_KRB5SERVICE, (void *)&service, &servicelen) < 0)
+       return AFPERR_MISC;
+
+    if (service == NULL) 
+       return AFPERR_MISC;
+
+    if (uam_afpserver_option(obj, UAM_OPTION_SESSIONINFO, (void *)&sinfo, NULL) < 0)
+       return AFPERR_MISC;
+
+    if (sinfo->sessionkey == NULL || sinfo->sessionkey_len == 0) {
+        LOG(log_info, logtype_uams, "internal error: sessionkey not set");
+        return AFPERR_PARAM;
+    }
+
+    /* We skip past the 'username' parameter because all that matters is the ticket */
+    p = ibuf;
+    while( *ibuf && ibuflen ) { ibuf++, ibuflen--; }
+    if (ibuflen < 4) {
+        LOG(log_info, logtype_uams, "uams_gss.c :LoginCont: user is %s, no ticket", p);
+       return AFPERR_PARAM;
+    }
+
+    ibuf++, ibuflen--; /* null termination */
+
+    if ((ibuf - p + 1) % 2) ibuf++, ibuflen--; /* deal with potential padding */
+
+    LOG(log_info, logtype_uams, "uams_gss.c :LoginCont: client thinks user is %s", p);
+
+    memcpy(&ticket_len, ibuf, sizeof(ticket_len));
+    ibuf += sizeof(ticket_len); ibuflen -= sizeof(ticket_len);
+    ticket_len = ntohs( ticket_len );
+
+    if (ticket_len > ibuflen) {
+        LOG(log_info, logtype_uams, "uams_gss.c :LoginCont: invalid ticket length (%u > %u)", ticket_len, ibuflen);
+       return AFPERR_PARAM;
+    }
+
+    if (!do_gss_auth(service, ibuf, ticket_len, rbuf, &rblen, username, userlen, sinfo)) {
+       char *at = strchr( username, '@' );
+
+       // Chop off the realm name
+       if (at)
+           *at = '\0';
+       if((pwd = uam_getname( obj, username, userlen )) == NULL) {
+           LOG(log_info, logtype_uams, "uam_getname() failed for %s", username);
+           return AFPERR_PARAM;
+       }
+       if (uam_checkuser(pwd) < 0) {
+           LOG(log_info, logtype_uams, "%s not a valid user", username);
+           return AFPERR_NOTAUTH;
+       }
+       *rbuflen = rblen;
+       *uam_pwd = pwd;
+       return AFP_OK;
+    } else {
+       LOG(log_info, logtype_uams, "do_gss_auth failed" );
+       *rbuflen = 0;
+        return AFPERR_MISC;
+    }
+}
+
+/*
+ * For the krb5 uam, this function only needs to return a two-byte
+ * login-session id. None of the data provided by the client up to this
+ * point is trustworthy as we'll have a signed ticket to parse in logincont.
+ */
+static int gss_login_ext(void *obj, char *uname, struct passwd **uam_pwd,
+                    char *ibuf, int ibuflen,
+                    char *rbuf, int *rbuflen)
+{
+    u_int16_t  temp16;
+
+    *rbuflen = 0;
+
+    /* The reply contains a two-byte ID value - note 
+     * that Apple's implementation seems to always return 1 as well
+     */
+    temp16 = htons( 1 );
+    memcpy(rbuf, &temp16, sizeof(temp16));
+    *rbuflen += sizeof(temp16);
+    return AFPERR_AUTHCONT;
+}
+
+/* logout */
+static void gss_logout() {
+}
+
+int uam_setup(const char *path)
+{
+    if (uam_register(UAM_SERVER_LOGIN_EXT, path, "Client Krb v2", 
+                  gss_login, gss_logincont, gss_logout, gss_login_ext) < 0)
+    if (uam_register(UAM_SERVER_LOGIN, path, "Client Krb v2", 
+                  gss_login, gss_logincont, gss_logout) < 0)
+        return -1;
+
+  return 0;
+}
+
+static void uam_cleanup(void)
+{
+  uam_unregister(UAM_SERVER_LOGIN_EXT, "Client Krb v2");
+}
+
+UAM_MODULE_EXPORT struct uam_export uams_gss = {
+  UAM_MODULE_SERVER,
+  UAM_MODULE_VERSION,
+  uam_setup, uam_cleanup
+};
+#endif