]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - scheduler/auth.c
Import CUPS 1.4svn r7023 into easysw/current.
[thirdparty/cups.git] / scheduler / auth.c
index 405c064a20a57d56df298d932de4628a27c6559c..d77d075d61566d87cc6b38fb6fc677be262bc8d9 100644 (file)
@@ -1,25 +1,19 @@
-;/*
- * "$Id: auth.c 4906 2006-01-10 20:53:28Z mike $"
+/*
+ * "$Id: auth.c 6947 2007-09-12 21:09:49Z mike $"
  *
  *   Authorization routines for the Common UNIX Printing System (CUPS).
  *
- *   Copyright 1997-2006 by Easy Software Products, all rights reserved.
- *
- *   These coded instructions, statements, and computer programs are the
- *   property of Easy Software Products and are protected by Federal
- *   copyright law.  Distribution and use rights are outlined in the file
- *   "LICENSE.txt" which should have been included with this file.  If this
- *   file is missing or damaged please contact Easy Software Products
- *   at:
+ *   Copyright 2007 by Apple Inc.
+ *   Copyright 1997-2007 by Easy Software Products, all rights reserved.
  *
- *       Attn: CUPS Licensing Information
- *       Easy Software Products
- *       44141 Airport View Drive, Suite 204
- *       Hollywood, Maryland 20636 USA
+ *   This file contains Kerberos support code, copyright 2006 by
+ *   Jelmer Vernooij.
  *
- *       Voice: (301) 373-9600
- *       EMail: cups-info@cups.org
- *         WWW: http://www.cups.org
+ *   These coded instructions, statements, and computer programs are the
+ *   property of Apple Inc. and are protected by Federal copyright
+ *   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
+ *   which should have been included with this file.  If this file is
+ *   file is missing or damaged, see the license at "http://www.cups.org/".
  *
  * Contents:
  *
  *   cupsdAddName()            - Add a name to a location...
  *   cupsdAllowHost()          - Add a host name that is allowed to access the
  *                               location.
- *   cupsdAllowIP()            - Add an IP address or network that is allowed to
- *                               access the location.
+ *   cupsdAllowIP()            - Add an IP address or network that is allowed
+ *                               to access the location.
+ *   cupsdAuthorize()          - Validate any authorization credentials.
  *   cupsdCheckAuth()          - Check authorization masks.
  *   cupsdCheckGroup()         - Check for a user's group membership.
  *   cupsdCopyLocation()       - Make a copy of a location...
- *   cupsdDeleteAllLocations() - Free all memory used for location authorization.
+ *   cupsdDeleteAllLocations() - Free all memory used for location
+ *                               authorization.
  *   cupsdDeleteLocation()     - Free all memory used by a location.
- *   cupsdDenyHost()           - Add a host name that is not allowed to access the
- *                               location.
- *   cupsdDenyIP()             - Add an IP address or network that is not allowed
- *                               to access the location.
+ *   cupsdDenyHost()           - Add a host name that is not allowed to access
+ *                               the location.
+ *   cupsdDenyIP()             - Add an IP address or network that is not
+ *                               allowed to access the location.
  *   cupsdFindBest()           - Find the location entry that best matches the
  *                               resource.
  *   cupsdFindLocation()       - Find the named location.
- *   cupsdGetMD5Passwd()       - Get an MD5 password.
  *   cupsdIsAuthorized()       - Check to see if the user is authorized...
  *   add_allow()               - Add an allow mask to the location.
  *   add_deny()                - Add a deny mask to the location.
+ *   compare_locations()       - Compare two locations.
  *   cups_crypt()              - Encrypt the password using the DES or MD5
  *                               algorithms, as needed.
+ *   get_gss_creds()           - Obtain GSS credentials.
+ *   get_md5_password()        - Get an MD5 password.
  *   pam_func()                - PAM conversation function.
  *   to64()                    - Base64-encode an integer value...
+ *   check_authref()           - Check an authorization services reference.
  */
 
 /*
 #ifdef HAVE_USERSEC_H
 #  include <usersec.h>
 #endif /* HAVE_USERSEC_H */
+#ifdef HAVE_MEMBERSHIP_H
+#  include <membership.h>
+#endif /* HAVE_MEMBERSHIP_H */
+#ifdef HAVE_AUTHORIZATION_H
+#  include <Security/AuthorizationTags.h>
+#  ifdef HAVE_SECBASEPRIV_H
+#    include <Security/SecBasePriv.h>
+#  else
+extern const char *cssmErrorString(int error);
+#  endif /* HAVE_SECBASEPRIV_H */
+#endif /* HAVE_AUTHORIZATION_H */
+#ifdef HAVE_SYS_PARAM_H
+#  include <sys/param.h>
+#endif /* HAVE_SYS_PARAM_H */
+#ifdef HAVE_SYS_UCRED_H
+#  include <sys/ucred.h>
+typedef struct xucred cupsd_ucred_t;
+#  define CUPSD_UCRED_UID(c) (c).cr_uid
+#else
+typedef struct ucred cupsd_ucred_t;
+#  define CUPSD_UCRED_UID(c) (c).uid
+#endif /* HAVE_SYS_UCRED_H */
 
 
 /*
 
 static cupsd_authmask_t        *add_allow(cupsd_location_t *loc);
 static cupsd_authmask_t        *add_deny(cupsd_location_t *loc);
-#if !HAVE_LIBPAM
+#ifdef HAVE_AUTHORIZATION_H
+static int             check_authref(cupsd_client_t *con, const char *right);
+#endif /* HAVE_AUTHORIZATION_H */
+static int             compare_locations(cupsd_location_t *a,
+                                         cupsd_location_t *b);
+#if !HAVE_LIBPAM && !defined(HAVE_USERSEC_H)
 static char            *cups_crypt(const char *pw, const char *salt);
-#endif /* !HAVE_LIBPAM */
+#endif /* !HAVE_LIBPAM && !HAVE_USERSEC_H */
+#ifdef HAVE_GSSAPI
+static gss_cred_id_t   get_gss_creds(const char *service_name);
+#endif /* HAVE_GSSAPI */
+static char            *get_md5_password(const char *username,
+                                         const char *group, char passwd[33]);
 #if HAVE_LIBPAM
 static int             pam_func(int, const struct pam_message **,
                                 struct pam_response **, void *);
-#else
+#elif !defined(HAVE_USERSEC_H)
 static void            to64(char *s, unsigned long v, int n);
 #endif /* HAVE_LIBPAM */
 
@@ -109,8 +140,8 @@ typedef struct cupsd_authdata_s             /**** Authentication data ****/
  * Local globals...
  */
 
-#if defined(__hpux) && defined(HAVE_LIBPAM)
-static cupsd_authdata_t        *auth_datat;    /* Current client being authenticated */
+#if defined(__hpux) && HAVE_LIBPAM
+static cupsd_authdata_t        *auth_data    /* Current client being authenticated */
 #endif /* __hpux && HAVE_LIBPAM */
 
 
@@ -125,30 +156,32 @@ cupsdAddLocation(const char *location)    /* I - Location path */
 
 
  /*
-  * Try to allocate memory for the new location.
+  * Make sure the locations array is created...
   */
 
-  if (NumLocations == 0)
-    temp = malloc(sizeof(cupsd_location_t));
-  else
-    temp = realloc(Locations, sizeof(cupsd_location_t) * (NumLocations + 1));
+  if (!Locations)
+    Locations = cupsArrayNew((cups_array_func_t)compare_locations, NULL);
 
-  if (temp == NULL)
+  if (!Locations)
     return (NULL);
 
-  Locations = temp;
-  temp      += NumLocations;
-  NumLocations ++;
+ /*
+  * Try to allocate memory for the new location.
+  */
+
+  if ((temp = calloc(1, sizeof(cupsd_location_t))) == NULL)
+    return (NULL);
 
  /*
   * Initialize the record and copy the name over...
   */
 
-  memset(temp, 0, sizeof(cupsd_location_t));
-  strlcpy(temp->location, location, sizeof(temp->location));
-  temp->length = strlen(temp->location);
+  temp->location = strdup(location);
+  temp->length   = strlen(temp->location);
+
+  cupsArrayAdd(Locations, temp);
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdAddLocation: added location \'%s\'",
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdAddLocation: added location \'%s\'",
                   location);
 
  /*
@@ -181,7 +214,7 @@ cupsdAddName(cupsd_location_t *loc, /* I - Location to add to */
   if (temp == NULL)
   {
     cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to add name to location %s: %s",
-                    loc->location, strerror(errno));
+                    loc->location ? loc->location : "nil", strerror(errno));
     return;
   }
 
@@ -191,7 +224,7 @@ cupsdAddName(cupsd_location_t *loc, /* I - Location to add to */
   {
     cupsdLogMessage(CUPSD_LOG_ERROR,
                     "Unable to duplicate name for location %s: %s",
-                    loc->location, strerror(errno));
+                    loc->location ? loc->location : "nil", strerror(errno));
     return;
   }
 
@@ -213,12 +246,12 @@ cupsdAllowHost(cupsd_location_t *loc,     /* I - Location to add to */
 
 
   cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdAllowHost(loc=%p(%s), name=\"%s\")",
-                  loc, loc->location, name);
+                  loc, loc->location ? loc->location : "nil", name);
 
   if ((temp = add_allow(loc)) == NULL)
     return;
 
-  if (strcasecmp(name, "@LOCAL") == 0)
+  if (!strcasecmp(name, "@LOCAL"))
   {
    /*
     * Allow *interface*...
@@ -228,7 +261,7 @@ cupsdAllowHost(cupsd_location_t *loc,       /* I - Location to add to */
     temp->mask.name.name   = strdup("*");
     temp->mask.name.length = 1;
   }
-  else if (strncasecmp(name, "@IF(", 4) == 0)
+  else if (!strncasecmp(name, "@IF(", 4))
   {
    /*
     * Allow *interface*...
@@ -276,9 +309,9 @@ cupsdAllowIP(cupsd_location_t *loc, /* I - Location to add to */
 
   cupsdLogMessage(CUPSD_LOG_DEBUG2,
                   "cupsdAllowIP(loc=%p(%s), address=%x:%x:%x:%x, netmask=%x:%x:%x:%x)",
-                 loc, loc->location, address[0], address[1], address[2],
-                 address[3], netmask[0], netmask[1], netmask[2],
-                 netmask[3]);
+                 loc, loc->location ? loc->location : "nil",
+                 address[0], address[1], address[2], address[3],
+                 netmask[0], netmask[1], netmask[2], netmask[3]);
 
   if ((temp = add_allow(loc)) == NULL)
     return;
@@ -297,9 +330,9 @@ void
 cupsdAuthorize(cupsd_client_t *con)    /* I - Client connection */
 {
   int          type;                   /* Authentication type */
-  char         *authorization,         /* Pointer into Authorization string */
-               *ptr,                   /* Pointer into string */
-               username[65],           /* Username string */
+  const char   *authorization;         /* Pointer into Authorization string */
+  char         *ptr,                   /* Pointer into string */
+               username[256],          /* Username string */
                password[33];           /* Password string */
   const char   *localuser;             /* Certificate username */
   char         nonce[HTTP_MAX_VALUE],  /* Nonce value from client */
@@ -336,7 +369,12 @@ cupsdAuthorize(cupsd_client_t *con)        /* I - Client connection */
                   con->uri, con->best, con->best ? con->best->location : "");
 
   if (con->best && con->best->type != AUTH_NONE)
-    type = con->best->type;
+  {
+    if (con->best->type == AUTH_DEFAULT)
+      type = DefaultAuthType;
+    else
+      type = con->best->type;
+  }
   else
     type = DefaultAuthType;
 
@@ -344,7 +382,7 @@ cupsdAuthorize(cupsd_client_t *con) /* I - Client connection */
   * Decode the Authorization string...
   */
 
-  authorization = con->http.fields[HTTP_FIELD_AUTHORIZATION];
+  authorization = httpGetField(&con->http, HTTP_FIELD_AUTHORIZATION);
 
   cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdAuthorize: Authorization=\"%s\"",
                   authorization);
@@ -352,6 +390,14 @@ cupsdAuthorize(cupsd_client_t *con)        /* I - Client connection */
   username[0] = '\0';
   password[0] = '\0';
 
+#ifdef HAVE_AUTHORIZATION_H
+  if (con->authref)
+  {
+    AuthorizationFree(con->authref, kAuthorizationFlagDefaults);
+    con->authref = NULL;
+  }
+#endif /* HAVE_AUTHORIZATION_H */
+
   if (type == AUTH_NONE)
   {
    /*
@@ -372,6 +418,116 @@ cupsdAuthorize(cupsd_client_t *con)       /* I - Client connection */
                     "cupsdAuthorize: No authentication data provided.");
     return;
   }
+#ifdef HAVE_AUTHORIZATION_H
+  else if (!strncmp(authorization, "AuthRef", 6) && 
+           !strcasecmp(con->http.hostname, "localhost"))
+  {
+    OSStatus           status;         /* Status */
+    int                        authlen;        /* Auth string length */
+    AuthorizationItemSet *authinfo;    /* Authorization item set */
+
+   /*
+    * Get the Authorization Services data...
+    */
+
+    authorization += 7;
+    while (isspace(*authorization & 255))
+      authorization ++;
+
+    authlen = sizeof(nonce);
+    httpDecode64_2(nonce, &authlen, authorization);
+
+    if (authlen != kAuthorizationExternalFormLength)
+    {
+      cupsdLogMessage(CUPSD_LOG_ERROR,
+                     "External Authorization reference size is incorrect!");
+      return;
+    }
+
+    if ((status = AuthorizationCreateFromExternalForm(
+                     (AuthorizationExternalForm *)nonce, &con->authref)) != 0)
+    {
+      cupsdLogMessage(CUPSD_LOG_ERROR,
+                     "AuthorizationCreateFromExternalForm returned %d (%s)",
+                     (int)status, cssmErrorString(status));
+      return;
+    }
+
+    if ((status = AuthorizationCopyInfo(con->authref, 
+                                       kAuthorizationEnvironmentUsername, 
+                                       &authinfo)) != 0)
+    {
+      cupsdLogMessage(CUPSD_LOG_ERROR,
+                     "AuthorizationCopyInfo returned %d (%s)",
+                     (int)status, cssmErrorString(status));
+      return;
+    }
+  
+    if (authinfo->count == 1)
+      strlcpy(username, authinfo->items[0].value, sizeof(username));
+
+    cupsdLogMessage(CUPSD_LOG_DEBUG,
+                    "cupsdAuthorize: Authorized as %s using AuthRef",
+                   username);
+
+    AuthorizationFreeItemSet(authinfo);
+  }
+#endif /* HAVE_AUTHORIZATION_H */
+#if defined(SO_PEERCRED) && defined(AF_LOCAL)
+  else if (!strncmp(authorization, "PeerCred ", 9) &&
+           con->http.hostaddr->addr.sa_family == AF_LOCAL)
+  {
+   /*
+    * Use peer credentials from domain socket connection...
+    */
+
+    struct passwd      *pwd;           /* Password entry for this user */
+    cupsd_ucred_t      peercred;       /* Peer credentials */
+    socklen_t          peersize;       /* Size of peer credentials */
+
+
+    if ((pwd = getpwnam(authorization + 9)) == NULL)
+    {
+      cupsdLogMessage(CUPSD_LOG_ERROR, "User \"%s\" does not exist!",
+                      authorization + 9);
+      return;
+    }
+
+    peersize = sizeof(peercred);
+
+    if (getsockopt(con->http.fd, SOL_SOCKET, SO_PEERCRED, &peercred, &peersize))
+    {
+      cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to get peer credentials - %s",
+                      strerror(errno));
+      return;
+    }
+
+    if (pwd->pw_uid != CUPSD_UCRED_UID(peercred))
+    {
+      cupsdLogMessage(CUPSD_LOG_ERROR,
+                      "Invalid peer credentials for \"%s\" - got %d, "
+                     "expected %d!", authorization + 9,
+                     CUPSD_UCRED_UID(peercred), pwd->pw_uid);
+#  ifdef HAVE_SYS_UCRED_H
+      cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdAuthorize: cr_version=%d",
+                      peercred.cr_version);
+      cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdAuthorize: cr_uid=%d",
+                      peercred.cr_uid);
+      cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdAuthorize: cr_ngroups=%d",
+                      peercred.cr_ngroups);
+      cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdAuthorize: cr_groups[0]=%d",
+                      peercred.cr_groups[0]);
+#  endif /* HAVE_SYS_UCRED_H */
+      return;
+    }
+
+    strlcpy(username, authorization + 9, sizeof(username));
+
+    cupsdLogMessage(CUPSD_LOG_DEBUG,
+                    "cupsdAuthorize: Authorized as %s using PeerCred",
+                   username);
+  }
+#endif /* SO_PEERCRED && AF_LOCAL */
   else if (!strncmp(authorization, "Local", 5) &&
            !strcasecmp(con->http.hostname, "localhost"))
   {
@@ -380,11 +536,17 @@ cupsdAuthorize(cupsd_client_t *con)       /* I - Client connection */
     */
 
     authorization += 5;
-    while (isspace(*authorization))
+    while (isspace(*authorization & 255))
       authorization ++;
 
     if ((localuser = cupsdFindCert(authorization)) != NULL)
+    {
       strlcpy(username, localuser, sizeof(username));
+
+      cupsdLogMessage(CUPSD_LOG_DEBUG,
+                     "cupsdAuthorize: Authorized as %s using Local",
+                     username);
+    }
     else
     {
       cupsdLogMessage(CUPSD_LOG_ERROR,
@@ -404,7 +566,7 @@ cupsdAuthorize(cupsd_client_t *con) /* I - Client connection */
 
 
     authorization += 5;
-    while (isspace(*authorization))
+    while (isspace(*authorization & 255))
       authorization ++;
 
     userlen = sizeof(username);
@@ -470,7 +632,13 @@ cupsdAuthorize(cupsd_client_t *con)        /* I - Client connection */
             strlcpy(data.username, username, sizeof(data.username));
            strlcpy(data.password, password, sizeof(data.password));
 
+#  if defined(__sun) || defined(__hpux)
+           pamdata.conv        = (int (*)(int, struct pam_message **,
+                                          struct pam_response **,
+                                          void *))pam_func;
+#  else
            pamdata.conv        = pam_func;
+#  endif /* __sun || __hpux */
            pamdata.appdata_ptr = &data;
 
 #  ifdef __hpux
@@ -522,7 +690,6 @@ cupsdAuthorize(cupsd_client_t *con) /* I - Client connection */
            */
 
            char        *authmsg;       /* Authentication message */
-           char        *loginmsg;      /* Login message */
            int         reenter;        /* ??? */
 
 
@@ -564,7 +731,7 @@ cupsdAuthorize(cupsd_client_t *con) /* I - Client connection */
              cupsdLogMessage(CUPSD_LOG_ERROR,
                              "cupsdAuthorize: Unknown username \"%s\"!",
                              username);
-             return (HTTP_UNAUTHORIZED);
+             return;
            }
 
 #  ifdef HAVE_SHADOW_H
@@ -641,6 +808,10 @@ cupsdAuthorize(cupsd_client_t *con)        /* I - Client connection */
            }
 #endif /* HAVE_LIBPAM */
           }
+
+         cupsdLogMessage(CUPSD_LOG_DEBUG,
+                         "cupsdAuthorize: Authorized as %s using Basic",
+                         username);
           break;
 
       case AUTH_BASICDIGEST :
@@ -648,7 +819,7 @@ cupsdAuthorize(cupsd_client_t *con) /* I - Client connection */
          * Do Basic authentication with the Digest password file...
          */
 
-         if (!cupsdGetMD5Passwd(username, NULL, md5))
+         if (!get_md5_password(username, NULL, md5))
          {
             cupsdLogMessage(CUPSD_LOG_ERROR,
                            "cupsdAuthorize: Unknown MD5 username \"%s\"!",
@@ -665,6 +836,10 @@ cupsdAuthorize(cupsd_client_t *con)        /* I - Client connection */
                            username);
             return;
          }
+
+         cupsdLogMessage(CUPSD_LOG_DEBUG,
+                         "cupsdAuthorize: Authorized as %s using BasicDigest",
+                         username);
          break;
     }
   }
@@ -719,7 +894,7 @@ cupsdAuthorize(cupsd_client_t *con) /* I - Client connection */
     * Validate the username and password...
     */
 
-    if (!cupsdGetMD5Passwd(username, NULL, md5))
+    if (!get_md5_password(username, NULL, md5))
     {
       cupsdLogMessage(CUPSD_LOG_ERROR,
                      "cupsdAuthorize: Unknown MD5 username \"%s\"!",
@@ -736,11 +911,173 @@ cupsdAuthorize(cupsd_client_t *con)      /* I - Client connection */
                      username);
       return;
     }
+
+    cupsdLogMessage(CUPSD_LOG_DEBUG,
+                    "cupsdAuthorize: Authorized as %s using Digest",
+                   username);
   }
-  else
+#ifdef HAVE_GSSAPI
+  else if (!strncmp(authorization, "Negotiate", 9)) 
   {
-    cupsdLogMessage(CUPSD_LOG_DEBUG,
-                    "cupsdAuthorize: Bad authentication data.");
+    int                        len;            /* Length of authorization string */
+    gss_cred_id_t      server_creds;   /* Server credentials */
+    gss_ctx_id_t       context;        /* Authorization context */
+    OM_uint32          major_status,   /* Major status code */
+                       minor_status;   /* Minor status code */
+    gss_buffer_desc    input_token = GSS_C_EMPTY_BUFFER,
+                                       /* Input token from string */
+                       output_token = GSS_C_EMPTY_BUFFER;
+                                       /* Output token for username */
+    gss_name_t         client_name;    /* Client name */
+    unsigned int       ret_flags;      /* Credential flags */
+
+
+#  ifdef __APPLE__
+   /*
+    * If the weak-linked GSSAPI/Kerberos library is not present, don't try
+    * to use it...
+    */
+
+    if (gss_init_sec_context == NULL)
+    {
+      cupsdLogMessage(CUPSD_LOG_WARN,
+                      "GSSAPI/Kerberos authentication failed because the "
+                     "Kerberos framework is not present.");
+      return;
+    }
+#  endif /* __APPLE__ */
+
+    con->gss_output_token.length = 0;
+
+   /*
+    * Find the start of the Kerberos input token...
+    */
+
+    authorization += 9;
+    while (isspace(*authorization & 255))
+      authorization ++;
+
+    if (!*authorization)
+    {
+      cupsdLogMessage(CUPSD_LOG_DEBUG2,
+                      "cupsdAuthorize: No authentication data specified.");
+      return;
+    }
+
+   /*
+    * Get the server credentials...
+    */
+
+    if ((server_creds = get_gss_creds(GSSServiceName)) == NULL)
+      return;  
+
+   /*
+    * Decode the authorization string to get the input token...
+    */
+
+    len                = strlen(authorization);
+    input_token.value  = malloc(len);
+    input_token.value  = httpDecode64_2(input_token.value, &len,
+                                       authorization);
+    input_token.length = len;
+
+   /*
+    * Accept the input token to get the authorization info...
+    */
+
+    context      = GSS_C_NO_CONTEXT;
+    client_name  = GSS_C_NO_NAME;
+    major_status = gss_accept_sec_context(&minor_status,
+                                         &context,
+                                         server_creds, 
+                                         &input_token,
+                                         GSS_C_NO_CHANNEL_BINDINGS,
+                                         &client_name,
+                                         NULL,
+                                         &con->gss_output_token,
+                                         &ret_flags,
+                                         NULL,
+                                         &con->gss_delegated_cred);
+
+    if (GSS_ERROR(major_status))
+    {
+      cupsdLogGSSMessage(CUPSD_LOG_DEBUG, major_status, minor_status,
+                         "cupsdAuthorize: Error accepting GSSAPI security "
+                        "context");
+
+      if (context != GSS_C_NO_CONTEXT)
+       gss_delete_sec_context(&minor_status, &context, GSS_C_NO_BUFFER);
+
+      gss_release_cred(&minor_status, &server_creds);
+      return;
+    }
+
+   /*
+    * Release our credentials...
+    */
+
+    gss_release_cred(&minor_status, &server_creds);
+
+   /*
+    * Get the username associated with the client's credentials...
+    */
+
+    if (!con->gss_delegated_cred)
+      cupsdLogMessage(CUPSD_LOG_DEBUG,
+                      "cupsdAuthorize: No delegated credentials!");
+
+    if (major_status == GSS_S_CONTINUE_NEEDED)
+      cupsdLogGSSMessage(CUPSD_LOG_DEBUG, major_status, minor_status,
+                         "cupsdAuthorize: Credentials not complete");
+    else if (major_status == GSS_S_COMPLETE)
+    {
+      major_status = gss_display_name(&minor_status, client_name, 
+                                     &output_token, NULL);
+
+      if (GSS_ERROR(major_status))
+      {
+       cupsdLogGSSMessage(CUPSD_LOG_DEBUG, major_status, minor_status,
+                           "cupsdAuthorize: Error getting username");
+       gss_release_name(&minor_status, &client_name);
+       gss_delete_sec_context(&minor_status, &context, GSS_C_NO_BUFFER);
+       return;
+      }
+
+      gss_release_name(&minor_status, &client_name);
+      strlcpy(username, output_token.value, sizeof(username));
+
+      cupsdLogMessage(CUPSD_LOG_DEBUG,
+                     "cupsdAuthorize: Authorized as %s using Negotiate",
+                     username);
+
+      gss_release_buffer(&minor_status, &output_token);
+      gss_delete_sec_context(&minor_status, &context, GSS_C_NO_BUFFER);
+
+      con->gss_have_creds = 1;
+    }
+    else
+      gss_release_name(&minor_status, &client_name);
+  }
+#endif /* HAVE_GSSAPI */
+  else if (type != AUTH_NONE)
+  {
+    char       scheme[256];            /* Auth scheme... */
+    static const char * const types[] =        /* Auth types */
+    {
+      "None",
+      "Basic",
+      "Digest",
+      "BasicDigest",
+      "Negotiate"
+    };
+
+
+    if (sscanf(authorization, "%255s", scheme) != 1)
+      strcpy(scheme, "UNKNOWN");
+
+    cupsdLogMessage(CUPSD_LOG_ERROR,
+                    "Bad authentication data \"%s ...\", expected \"%s ...\"",
+                    scheme, types[type]);
     return;
   }
 
@@ -752,9 +1089,6 @@ cupsdAuthorize(cupsd_client_t *con)        /* I - Client connection */
 
   strlcpy(con->username, username, sizeof(con->username));
   strlcpy(con->password, password, sizeof(con->password));
-
-  cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdAuthorize: username=\"%s\"",
-                  con->username);
 }
 
 
@@ -803,7 +1137,9 @@ cupsdCheckAuth(
 
             cupsdNetIFUpdate();
 
-           for (iface = NetIFList; iface != NULL; iface = iface->next)
+           for (iface = (cupsd_netif_t *)cupsArrayFirst(NetIFList);
+                iface;
+                iface = (cupsd_netif_t *)cupsArrayNext(NetIFList))
            {
             /*
              * Only check local interfaces...
@@ -848,10 +1184,13 @@ cupsdCheckAuth(
            * Check the named interface...
            */
 
-            for (iface = cupsdNetIFFind(masks->mask.name.name);
-                iface && !strcasecmp(masks->mask.name.name, iface->name);
-                iface = iface->next)
+           for (iface = (cupsd_netif_t *)cupsArrayFirst(NetIFList);
+                iface;
+                iface = (cupsd_netif_t *)cupsArrayNext(NetIFList))
            {
+              if (strcmp(masks->mask.name.name, iface->name))
+                continue;
+
               if (iface->address.addr.sa_family == AF_INET)
              {
               /*
@@ -898,8 +1237,8 @@ cupsdCheckAuth(
 
          if (name_len >= masks->mask.name.length &&
              masks->mask.name.name[0] == '.' &&
-             strcasecmp(name + name_len - masks->mask.name.length,
-                        masks->mask.name.name) == 0)
+             !strcasecmp(name + name_len - masks->mask.name.length,
+                         masks->mask.name.name))
            return (1);
           break;
 
@@ -939,6 +1278,11 @@ cupsdCheckGroup(
   int                  i;              /* Looping var */
   struct group         *group;         /* System group info */
   char                 junk[33];       /* MD5 password (not used) */
+#ifdef HAVE_MBR_UID_TO_UUID
+  uuid_t               useruuid,       /* UUID for username */
+                       groupuuid;      /* UUID for groupname */
+  int                  is_member;      /* True if user is a member of group */
+#endif /* HAVE_MBR_UID_TO_UUID */
 
 
   cupsdLogMessage(CUPSD_LOG_DEBUG2,
@@ -978,13 +1322,26 @@ cupsdCheckGroup(
   if (user && group && group->gr_gid == user->pw_gid)
     return (1);
 
+#ifdef HAVE_MBR_UID_TO_UUID
+ /*
+  * Check group membership through MacOS X membership API...
+  */
+
+  if (user && group)
+    if (!mbr_uid_to_uuid(user->pw_uid, useruuid))
+      if (!mbr_gid_to_uuid(group->gr_gid, groupuuid))
+       if (!mbr_check_membership(useruuid, groupuuid, &is_member))
+         if (is_member)
+           return (1);
+#endif /* HAVE_MBR_UID_TO_UUID */
+
  /*
   * Username not found, group not found, or user is not part of the
   * system group...  Check for a user and group in the MD5 password
   * file...
   */
 
-  if (cupsdGetMD5Passwd(username, groupname, junk) != NULL)
+  if (get_md5_password(username, groupname, junk) != NULL)
     return (1);
 
  /*
@@ -1004,19 +1361,11 @@ cupsdCopyLocation(
     cupsd_location_t **loc)            /* IO - Original location */
 {
   int                  i;              /* Looping var */
-  int                  locindex;       /* Index into Locations array */
   cupsd_location_t     *temp;          /* New location */
   char                 location[HTTP_MAX_URI];
                                        /* Location of resource */
 
 
- /*
-  * Add the new location, updating the original location
-  * pointer as needed...
-  */
-
-  locindex = *loc - Locations;
-
  /*
   * Use a local copy of location because cupsdAddLocation may cause
   * this memory to be moved...
@@ -1027,8 +1376,6 @@ cupsdCopyLocation(
   if ((temp = cupsdAddLocation(location)) == NULL)
     return (NULL);
 
-  *loc = Locations + locindex;
-
  /*
   * Copy the information from the original location to the new one.
   */
@@ -1051,7 +1398,8 @@ cupsdCopyLocation(
       cupsdLogMessage(CUPSD_LOG_ERROR,
                       "cupsdCopyLocation: Unable to allocate memory for %d names: %s",
                       temp->num_names, strerror(errno));
-      NumLocations --;
+
+      cupsdDeleteLocation(temp);
       return (NULL);
     }
 
@@ -1062,7 +1410,7 @@ cupsdCopyLocation(
                        "cupsdCopyLocation: Unable to copy name \"%s\": %s",
                         (*loc)->names[i], strerror(errno));
 
-       NumLocations --;
+        cupsdDeleteLocation(temp);
        return (NULL);
       }
   }
@@ -1078,7 +1426,7 @@ cupsdCopyLocation(
       cupsdLogMessage(CUPSD_LOG_ERROR,
                       "cupsdCopyLocation: Unable to allocate memory for %d allow rules: %s",
                       temp->num_allow, strerror(errno));
-      NumLocations --;
+      cupsdDeleteLocation(temp);
       return (NULL);
     }
 
@@ -1094,7 +1442,7 @@ cupsdCopyLocation(
              cupsdLogMessage(CUPSD_LOG_ERROR,
                              "cupsdCopyLocation: Unable to copy allow name \"%s\": %s",
                              (*loc)->allow[i].mask.name.name, strerror(errno));
-             NumLocations --;
+              cupsdDeleteLocation(temp);
              return (NULL);
            }
            break;
@@ -1116,7 +1464,7 @@ cupsdCopyLocation(
       cupsdLogMessage(CUPSD_LOG_ERROR,
                       "cupsdCopyLocation: Unable to allocate memory for %d deny rules: %s",
                       temp->num_deny, strerror(errno));
-      NumLocations --;
+      cupsdDeleteLocation(temp);
       return (NULL);
     }
 
@@ -1132,7 +1480,7 @@ cupsdCopyLocation(
              cupsdLogMessage(CUPSD_LOG_ERROR,
                              "cupsdCopyLocation: Unable to copy deny name \"%s\": %s",
                              (*loc)->deny[i].mask.name.name, strerror(errno));
-             NumLocations --;
+              cupsdDeleteLocation(temp);
              return (NULL);
            }
            break;
@@ -1154,7 +1502,6 @@ cupsdCopyLocation(
 void
 cupsdDeleteAllLocations(void)
 {
-  int                  i;              /* Looping var */
   cupsd_location_t     *loc;           /* Current location */
 
 
@@ -1162,18 +1509,17 @@ cupsdDeleteAllLocations(void)
   * Free all of the allow/deny records first...
   */
 
-  for (i = NumLocations, loc = Locations; i > 0; i --, loc ++)
+  for (loc = (cupsd_location_t *)cupsArrayFirst(Locations);
+       loc;
+       loc = (cupsd_location_t *)cupsArrayNext(Locations))
     cupsdDeleteLocation(loc);
 
  /*
   * Then free the location array...
   */
 
-  if (NumLocations > 0)
-    free(Locations);
-
-  Locations    = NULL;
-  NumLocations = 0;
+  cupsArrayDelete(Locations);
+  Locations = NULL;
 }
 
 
@@ -1189,6 +1535,8 @@ cupsdDeleteLocation(
   cupsd_authmask_t     *mask;          /* Current mask */
 
 
+  cupsArrayRemove(Locations, loc);
+
   for (i = loc->num_names - 1; i >= 0; i --)
     free(loc->names[i]);
 
@@ -1208,6 +1556,9 @@ cupsdDeleteLocation(
 
   if (loc->num_deny > 0)
     free(loc->deny);
+
+  free(loc->location);
+  free(loc);
 }
 
 
@@ -1226,12 +1577,12 @@ cupsdDenyHost(cupsd_location_t *loc,    /* I - Location to add to */
 
 
   cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDenyHost(loc=%p(%s), name=\"%s\")",
-                  loc, loc->location, name);
+                  loc, loc->location ? loc->location : "nil", name);
 
   if ((temp = add_deny(loc)) == NULL)
     return;
 
-  if (strcasecmp(name, "@LOCAL") == 0)
+  if (!strcasecmp(name, "@LOCAL"))
   {
    /*
     * Deny *interface*...
@@ -1241,7 +1592,7 @@ cupsdDenyHost(cupsd_location_t *loc,      /* I - Location to add to */
     temp->mask.name.name   = strdup("*");
     temp->mask.name.length = 1;
   }
-  else if (strncasecmp(name, "@IF(", 4) == 0)
+  else if (!strncasecmp(name, "@IF(", 4))
   {
    /*
     * Deny *interface*...
@@ -1289,9 +1640,9 @@ cupsdDenyIP(cupsd_location_t *loc,        /* I - Location to add to */
 
   cupsdLogMessage(CUPSD_LOG_DEBUG,
                   "cupsdDenyIP(loc=%p(%s), address=%x:%x:%x:%x, netmask=%x:%x:%x:%x)",
-                 loc, loc->location, address[0], address[1], address[2],
-                 address[3], netmask[0], netmask[1], netmask[2],
-                 netmask[3]);
+                 loc, loc->location ? loc->location : "nil",
+                 address[0], address[1], address[2], address[3],
+                 netmask[0], netmask[1], netmask[2], netmask[3]);
 
   if ((temp = add_deny(loc)) == NULL)
     return;
@@ -1310,7 +1661,6 @@ cupsd_location_t *                        /* O - Location that matches */
 cupsdFindBest(const char   *path,      /* I - Resource path */
               http_state_t state)      /* I - HTTP state/request */
 {
-  int                  i;              /* Looping var */
   char                 uri[HTTP_MAX_URI],
                                        /* URI in request... */
                        *uriptr;        /* Pointer into URI */
@@ -1368,10 +1718,12 @@ cupsdFindBest(const char   *path,       /* I - Resource path */
   best    = NULL;
   bestlen = 0;
 
-  for (i = NumLocations, loc = Locations; i > 0; i --, loc ++)
+  for (loc = (cupsd_location_t *)cupsArrayFirst(Locations);
+       loc;
+       loc = (cupsd_location_t *)cupsArrayNext(Locations))
   {
     cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdFindBest: Location %s Limit %x",
-                    loc->location, loc->limit);
+                    loc->location ? loc->location : "nil", loc->limit);
 
     if (!strncmp(uri, "/printers/", 10) || !strncmp(uri, "/classes/", 9))
     {
@@ -1379,8 +1731,8 @@ cupsdFindBest(const char   *path, /* I - Resource path */
       * Use case-insensitive comparison for queue names...
       */
 
-      if (loc->length > bestlen &&
-          strncasecmp(uri, loc->location, loc->length) == 0 &&
+      if (loc->length > bestlen && loc->location &&
+          !strncasecmp(uri, loc->location, loc->length) &&
          loc->location[0] == '/' &&
          (limit & loc->limit) != 0)
       {
@@ -1394,7 +1746,7 @@ cupsdFindBest(const char   *path, /* I - Resource path */
       * Use case-sensitive comparison for other URIs...
       */
 
-      if (loc->length > bestlen &&
+      if (loc->length > bestlen && loc->location &&
           !strncmp(uri, loc->location, loc->length) &&
          loc->location[0] == '/' &&
          (limit & loc->limit) != 0)
@@ -1423,80 +1775,12 @@ cupsdFindBest(const char   *path,       /* I - Resource path */
 cupsd_location_t *                     /* O - Location that matches */
 cupsdFindLocation(const char *location)        /* I - Connection */
 {
-  int          i;                      /* Looping var */
+  cupsd_location_t     key;            /* Search key */
 
 
- /*
-  * Loop through the list of locations to find a match...
-  */
-
-  for (i = 0; i < NumLocations; i ++)
-    if (!strcasecmp(Locations[i].location, location))
-      return (Locations + i);
+  key.location = (char *)location;
 
-  return (NULL);
-}
-
-
-/*
- * 'cupsdGetMD5Passwd()' - Get an MD5 password.
- */
-
-char *                                 /* O - MD5 password string */
-cupsdGetMD5Passwd(const char *username,        /* I - Username */
-                  const char *group,   /* I - Group */
-                  char       passwd[33])/* O - MD5 password string */
-{
-  cups_file_t  *fp;                    /* passwd.md5 file */
-  char         filename[1024],         /* passwd.md5 filename */
-               line[256],              /* Line from file */
-               tempuser[33],           /* User from file */
-               tempgroup[33];          /* Group from file */
-
-
-  cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                  "cupsdGetMD5Passwd(username=\"%s\", group=\"%s\", passwd=%p)",
-                  username, group ? group : "(null)", passwd);
-
-  snprintf(filename, sizeof(filename), "%s/passwd.md5", ServerRoot);
-  if ((fp = cupsFileOpen(filename, "r")) == NULL)
-  {
-    if (errno != ENOENT)
-      cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to open %s - %s", filename,
-                      strerror(errno));
-
-    return (NULL);
-  }
-
-  while (cupsFileGets(fp, line, sizeof(line)) != NULL)
-  {
-    if (sscanf(line, "%32[^:]:%32[^:]:%32s", tempuser, tempgroup, passwd) != 3)
-    {
-      cupsdLogMessage(CUPSD_LOG_ERROR, "Bad MD5 password line: %s", line);
-      continue;
-    }
-
-    if (strcmp(username, tempuser) == 0 &&
-        (group == NULL || strcmp(group, tempgroup) == 0))
-    {
-     /*
-      * Found the password entry!
-      */
-
-      cupsdLogMessage(CUPSD_LOG_DEBUG2, "Found MD5 user %s, group %s...",
-                      username, tempgroup);
-
-      cupsFileClose(fp);
-      return (passwd);
-    }
-  }
-
- /*
-  * Didn't find a password entry - return NULL!
-  */
-
-  cupsFileClose(fp);
-  return (NULL);
+  return ((cupsd_location_t *)cupsArrayFind(Locations, &key));
 }
 
 
@@ -1513,6 +1797,9 @@ cupsdIsAuthorized(cupsd_client_t *con,    /* I - Connection */
   unsigned             address[4];     /* Authorization address */
   cupsd_location_t     *best;          /* Best match for location so far */
   int                  hostlen;        /* Length of hostname */
+  char                 username[256],  /* Username to authorize */
+                       ownername[256], /* Owner name to authorize */
+                       *ptr;           /* Pointer into username */
   struct passwd                *pw;            /* User password data */
   static const char * const levels[] = /* Auth levels */
                {
@@ -1525,13 +1812,18 @@ cupsdIsAuthorized(cupsd_client_t *con,  /* I - Connection */
                  "NONE",
                  "BASIC",
                  "DIGEST",
-                 "BASICDIGEST"
+                 "BASICDIGEST",
+                 "KERBEROS"
                };
 
 
   cupsdLogMessage(CUPSD_LOG_DEBUG2,
                   "cupsdIsAuthorized: con->uri=\"%s\", con->best=%p(%s)",
-                  con->uri, con->best, con->best ? con->best->location : "");
+                  con->uri, con->best, con->best ? con->best->location ?
+                                          con->best->location : "(null)" : "");
+  if (owner)
+    cupsdLogMessage(CUPSD_LOG_DEBUG2,
+                    "cupsdIsAuthorized: owner=\"%s\"", owner);
 
  /*
   * If there is no "best" authentication rule for this request, then
@@ -1551,7 +1843,8 @@ cupsdIsAuthorized(cupsd_client_t *con,    /* I - Connection */
   best = con->best;
 
   cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                  "cupsdIsAuthorized: level=AUTH_%s, type=AUTH_%s, satisfy=AUTH_SATISFY_%s, num_names=%d",
+                  "cupsdIsAuthorized: level=AUTH_%s, type=AUTH_%s, "
+                 "satisfy=AUTH_SATISFY_%s, num_names=%d",
                   levels[best->level], types[best->type],
                  best->satisfy ? "ANY" : "ALL", best->num_names);
 
@@ -1596,7 +1889,7 @@ cupsdIsAuthorized(cupsd_client_t *con,    /* I - Connection */
   if (!strcasecmp(con->http.hostname, "localhost"))
   {
    /*
-    * Access from localhost (127.0.0.1 or :::1) is always allowed...
+    * Access from localhost (127.0.0.1 or ::1) is always allowed...
     */
 
     auth = AUTH_ALLOW;
@@ -1650,9 +1943,13 @@ cupsdIsAuthorized(cupsd_client_t *con,   /* I - Connection */
   * See if encryption is required...
   */
 
-  if (best->encryption >= HTTP_ENCRYPT_REQUIRED && !con->http.tls)
+  if ((best->encryption >= HTTP_ENCRYPT_REQUIRED && !con->http.tls &&
+      strcasecmp(con->http.hostname, "localhost") &&
+      best->satisfy == AUTH_SATISFY_ALL) &&
+      !(best->type == AUTH_NEGOTIATE || 
+        (best->type == AUTH_NONE && DefaultAuthType == AUTH_NEGOTIATE)))
   {
-    cupsdLogMessage(CUPSD_LOG_DEBUG2,
+    cupsdLogMessage(CUPSD_LOG_DEBUG,
                     "cupsdIsAuthorized: Need upgrade to TLS...");
     return (HTTP_UPGRADE_REQUIRED);
   }
@@ -1666,7 +1963,8 @@ cupsdIsAuthorized(cupsd_client_t *con,    /* I - Connection */
       (best->type == AUTH_NONE && best->num_names == 0))
     return (HTTP_OK);
 
-  if (best->type == AUTH_NONE && best->limit == AUTH_LIMIT_IPP)
+  if (!con->username[0] && best->type == AUTH_NONE &&
+      best->limit == AUTH_LIMIT_IPP)
   {
    /*
     * Check for unauthenticated username...
@@ -1678,38 +1976,72 @@ cupsdIsAuthorized(cupsd_client_t *con,  /* I - Connection */
     attr = ippFindAttribute(con->request, "requesting-user-name", IPP_TAG_NAME);
     if (attr)
     {
-      cupsdLogMessage(CUPSD_LOG_DEBUG2,
+      cupsdLogMessage(CUPSD_LOG_DEBUG,
                       "cupsdIsAuthorized: requesting-user-name=\"%s\"",
                       attr->values[0].string.text);
-      return (HTTP_OK);
+      strlcpy(username, attr->values[0].string.text, sizeof(username));
     }
-  }
-
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: username=\"%s\"",
-                 con->username);
-
-  if (!con->username[0])
-  {
-    if (best->satisfy == AUTH_SATISFY_ALL || auth == AUTH_DENY)
+    else if (best->satisfy == AUTH_SATISFY_ALL || auth == AUTH_DENY)
       return (HTTP_UNAUTHORIZED);      /* Non-anonymous needs user/pass */
     else
       return (HTTP_OK);                        /* unless overridden with Satisfy */
   }
+  else
+  {
+    cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdIsAuthorized: username=\"%s\"",
+                   con->username);
+
+#ifdef HAVE_AUTHORIZATION_H
+    if (!con->username[0] && !con->authref)
+#else
+    if (!con->username[0])
+#endif /* HAVE_AUTHORIZATION_H */
+    {
+      if (best->satisfy == AUTH_SATISFY_ALL || auth == AUTH_DENY)
+       return (HTTP_UNAUTHORIZED);     /* Non-anonymous needs user/pass */
+      else
+       return (HTTP_OK);               /* unless overridden with Satisfy */
+    }
+
+    strlcpy(username, con->username, sizeof(username));
+  }
 
  /*
-  * OK, the password is good.  See if we need normal user access, or group
+  * OK, got a username.  See if we need normal user access, or group
   * access... (root always matches)
   */
 
-  if (!strcmp(con->username, "root"))
+  if (!strcmp(username, "root"))
     return (HTTP_OK);
 
+ /*
+  * Strip any @domain or @KDC from the username and owner...
+  */
+
+  if ((ptr = strchr(username, '@')) != NULL)
+    *ptr = '\0';
+
+  if (owner)
+  {
+    strlcpy(ownername, owner, sizeof(ownername));
+
+    if ((ptr = strchr(ownername, '@')) != NULL)
+      *ptr = '\0';
+  }
+  else
+    ownername[0] = '\0';
+
  /*
   * Get the user info...
   */
 
-  pw = getpwnam(con->username);
-  endpwent();
+  if (username[0])
+  {
+    pw = getpwnam(username);
+    endpwent();
+  }
+  else
+    pw = NULL;
 
   if (best->level == AUTH_USER)
   {
@@ -1729,23 +2061,45 @@ cupsdIsAuthorized(cupsd_client_t *con,  /* I - Connection */
     cupsdLogMessage(CUPSD_LOG_DEBUG2,
                     "cupsdIsAuthorized: Checking user membership...");
 
+#ifdef HAVE_AUTHORIZATION_H
+   /*
+    * If an authorization reference was supplied it must match a right name...
+    */
+
+    if (con->authref)
+    {
+      for (i = 0; i < best->num_names; i ++)
+      {
+       if (!strncasecmp(best->names[i], "@AUTHKEY(", 9) && 
+           check_authref(con, best->names[i] + 9))
+         return (HTTP_OK);
+       else if (!strcasecmp(best->names[i], "@SYSTEM") &&
+                SystemGroupAuthKey &&
+                check_authref(con, SystemGroupAuthKey))
+         return (HTTP_OK);
+      }
+
+      return (HTTP_UNAUTHORIZED);
+    }
+#endif /* HAVE_AUTHORIZATION_H */
+
     for (i = 0; i < best->num_names; i ++)
     {
       if (!strcasecmp(best->names[i], "@OWNER") && owner &&
-          !strcasecmp(con->username, owner))
+          !strcasecmp(username, ownername))
        return (HTTP_OK);
       else if (!strcasecmp(best->names[i], "@SYSTEM"))
       {
         for (j = 0; j < NumSystemGroups; j ++)
-         if (cupsdCheckGroup(con->username, pw, SystemGroups[j]))
+         if (cupsdCheckGroup(username, pw, SystemGroups[j]))
            return (HTTP_OK);
       }
       else if (best->names[i][0] == '@')
       {
-        if (cupsdCheckGroup(con->username, pw, best->names[i] + 1))
+        if (cupsdCheckGroup(username, pw, best->names[i] + 1))
           return (HTTP_OK);
       }
-      else if (!strcasecmp(con->username, best->names[i]))
+      else if (!strcasecmp(username, best->names[i]))
         return (HTTP_OK);
     }
 
@@ -1772,10 +2126,10 @@ cupsdIsAuthorized(cupsd_client_t *con,  /* I - Connection */
     if (!strcasecmp(best->names[i], "@SYSTEM"))
     {
       for (j = 0; j < NumSystemGroups; j ++)
-       if (cupsdCheckGroup(con->username, pw, SystemGroups[j]))
+       if (cupsdCheckGroup(username, pw, SystemGroups[j]))
          return (HTTP_OK);
     }
-    else if (cupsdCheckGroup(con->username, pw, best->names[i]))
+    else if (cupsdCheckGroup(username, pw, best->names[i]))
       return (HTTP_OK);
   }
 
@@ -1783,7 +2137,7 @@ cupsdIsAuthorized(cupsd_client_t *con,    /* I - Connection */
   * The user isn't part of the specified group, so deny access...
   */
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2,
+  cupsdLogMessage(CUPSD_LOG_DEBUG,
                   "cupsdIsAuthorized: User not in group(s)!");
 
   return (HTTP_UNAUTHORIZED);
@@ -1874,7 +2228,72 @@ add_deny(cupsd_location_t *loc)          /* I - Location to add to */
 }
 
 
-#if !HAVE_LIBPAM
+#ifdef HAVE_AUTHORIZATION_H
+/*
+ * 'check_authref()' - Check if an authorization services reference has the
+ *                    supplied right.
+ */
+
+static int                             /* O - 1 if right is valid, 0 otherwise */
+check_authref(cupsd_client_t *con,     /* I - Connection */
+             const char     *right)    /* I - Right name */
+{
+  OSStatus             status;         /* OS Status */
+  AuthorizationItem    authright;      /* Authorization right */
+  AuthorizationRights  authrights;     /* Authorization rights */
+  AuthorizationFlags   authflags;      /* Authorization flags */
+
+
+ /*
+  * Check to see if the user is allowed to perform the task...
+  */
+
+  if (!con->authref)
+    return (0);
+
+  authright.name        = right;
+  authright.valueLength = 0;
+  authright.value       = NULL;
+  authright.flags       = 0;
+
+  authrights.count = 1;
+  authrights.items = &authright;
+
+  authflags = kAuthorizationFlagDefaults | 
+             kAuthorizationFlagExtendRights;
+
+  if ((status = AuthorizationCopyRights(con->authref, &authrights, 
+                                       kAuthorizationEmptyEnvironment, 
+                                       authflags, NULL)) != 0)
+  {
+    cupsdLogMessage(CUPSD_LOG_ERROR,
+                   "AuthorizationCopyRights(\"%s\") returned %d (%s)",
+                   authright.name, (int)status, cssmErrorString(status));
+    return (0);
+  }
+
+  cupsdLogMessage(CUPSD_LOG_DEBUG2,
+                  "AuthorizationCopyRights(\"%s\") succeeded!",
+                 authright.name);
+
+  return (1);
+}
+#endif /* HAVE_AUTHORIZATION_H */
+
+
+/*
+ * 'compare_locations()' - Compare two locations.
+ */
+
+static int                             /* O - Result of comparison */
+compare_locations(cupsd_location_t *a, /* I - First location */
+                  cupsd_location_t *b) /* I - Second location */
+{
+  return (strcmp(b->location, a->location));
+}
+
+
+#if !HAVE_LIBPAM && !defined(HAVE_USERSEC_H)
 /*
  * 'cups_crypt()' - Encrypt the password using the DES or MD5 algorithms,
  *                  as needed.
@@ -1884,7 +2303,7 @@ static char *                             /* O - Encrypted password */
 cups_crypt(const char *pw,             /* I - Password string */
            const char *salt)           /* I - Salt (key) string */
 {
-  if (strncmp(salt, "$1$", 3) == 0)
+  if (!strncmp(salt, "$1$", 3))
   {
    /*
     * Use MD5 passwords without the benefit of PAM; this is for
@@ -1918,45 +2337,45 @@ cups_crypt(const char *pw,              /* I - Password string */
 
     pwlen = strlen(pw);
 
-    _cups_md5_init(&state);
-    _cups_md5_append(&state, (unsigned char *)pw, pwlen);
-    _cups_md5_append(&state, (unsigned char *)salt, salt_end - salt);
+    _cupsMD5Init(&state);
+    _cupsMD5Append(&state, (unsigned char *)pw, pwlen);
+    _cupsMD5Append(&state, (unsigned char *)salt, salt_end - salt);
 
-    _cups_md5_init(&state2);
-    _cups_md5_append(&state2, (unsigned char *)pw, pwlen);
-    _cups_md5_append(&state2, (unsigned char *)salt + 3, salt_end - salt - 3);
-    _cups_md5_append(&state2, (unsigned char *)pw, pwlen);
-    _cups_md5_finish(&state2, digest);
+    _cupsMD5Init(&state2);
+    _cupsMD5Append(&state2, (unsigned char *)pw, pwlen);
+    _cupsMD5Append(&state2, (unsigned char *)salt + 3, salt_end - salt - 3);
+    _cupsMD5Append(&state2, (unsigned char *)pw, pwlen);
+    _cupsMD5Finish(&state2, digest);
 
     for (i = pwlen; i > 0; i -= 16)
-      _cups_md5_append(&state, digest, i > 16 ? 16 : i);
+      _cupsMD5Append(&state, digest, i > 16 ? 16 : i);
 
     for (i = pwlen; i > 0; i >>= 1)
-      _cups_md5_append(&state, (unsigned char *)((i & 1) ? "" : pw), 1);
+      _cupsMD5Append(&state, (unsigned char *)((i & 1) ? "" : pw), 1);
 
-    _cups_md5_finish(&state, digest);
+    _cupsMD5Finish(&state, digest);
 
     for (i = 0; i < 1000; i ++)
     {
-      _cups_md5_init(&state);
+      _cupsMD5Init(&state);
 
       if (i & 1)
-        _cups_md5_append(&state, (unsigned char *)pw, pwlen);
+        _cupsMD5Append(&state, (unsigned char *)pw, pwlen);
       else
-        _cups_md5_append(&state, digest, 16);
+        _cupsMD5Append(&state, digest, 16);
 
       if (i % 3)
-        _cups_md5_append(&state, (unsigned char *)salt + 3, salt_end - salt - 3);
+        _cupsMD5Append(&state, (unsigned char *)salt + 3, salt_end - salt - 3);
 
       if (i % 7)
-        _cups_md5_append(&state, (unsigned char *)pw, pwlen);
+        _cupsMD5Append(&state, (unsigned char *)pw, pwlen);
 
       if (i & 1)
-        _cups_md5_append(&state, digest, 16);
+        _cupsMD5Append(&state, digest, 16);
       else
-        _cups_md5_append(&state, (unsigned char *)pw, pwlen);
+        _cupsMD5Append(&state, (unsigned char *)pw, pwlen);
 
-      _cups_md5_finish(&state, digest);
+      _cupsMD5Finish(&state, digest);
     }
 
    /*
@@ -1994,7 +2413,144 @@ cups_crypt(const char *pw,              /* I - Password string */
     return (crypt(pw, salt));
   }
 }
-#endif /* !HAVE_LIBPAM */
+#endif /* !HAVE_LIBPAM && !HAVE_USERSEC_H */
+
+
+#ifdef HAVE_GSSAPI
+/*
+ * 'get_gss_creds()' - Obtain GSS credentials.
+ */
+
+static gss_cred_id_t                   /* O - Server credentials */
+get_gss_creds(const char *service_name)        /* I - Service name */
+{
+  OM_uint32    major_status,           /* Major status code */
+               minor_status;           /* Minor status code */
+  gss_name_t   server_name;            /* Server name */
+  gss_cred_id_t        server_creds;           /* Server credentials */
+  gss_buffer_desc token = GSS_C_EMPTY_BUFFER;
+                                       /* Service name token */
+  char         buf[1024],              /* Service name buffer */
+               fqdn[HTTP_MAX_URI];     /* Hostname of server */
+
+
+  snprintf(buf, sizeof(buf), "%s@%s", service_name,
+          httpGetHostname(NULL, fqdn, sizeof(fqdn)));
+
+  token.value  = buf;
+  token.length = strlen(buf);
+  server_name  = GSS_C_NO_NAME;
+  major_status = gss_import_name(&minor_status, &token,
+                                GSS_C_NT_HOSTBASED_SERVICE,
+                                &server_name);
+
+  memset(&token, 0, sizeof(token));
+
+  if (GSS_ERROR(major_status))
+  {
+    cupsdLogGSSMessage(CUPSD_LOG_WARN, major_status, minor_status, 
+                      "gss_import_name() failed");
+    return (NULL);
+  }
+
+  major_status = gss_display_name(&minor_status, server_name, &token, NULL);
+
+  if (GSS_ERROR(major_status))
+  {
+    cupsdLogGSSMessage(CUPSD_LOG_WARN, major_status, minor_status,
+                       "gss_display_name() failed"); 
+    return (NULL);
+  }
+
+  cupsdLogMessage(CUPSD_LOG_DEBUG,
+                  "get_gss_creds: Attempting to acquire credentials for %s...", 
+                  (char *)token.value);
+
+  server_creds = GSS_C_NO_CREDENTIAL;
+  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 (GSS_ERROR(major_status))
+  {
+    cupsdLogGSSMessage(CUPSD_LOG_WARN, major_status, minor_status,
+                       "gss_acquire_cred() failed"); 
+    gss_release_name(&minor_status, &server_name);
+    gss_release_buffer(&minor_status, &token);
+    return (NULL);
+  }
+
+  cupsdLogMessage(CUPSD_LOG_DEBUG,
+                  "get_gss_creds: Credentials acquired successfully for %s.", 
+                  (char *)token.value);
+
+  gss_release_name(&minor_status, &server_name);
+  gss_release_buffer(&minor_status, &token);
+
+  return (server_creds);
+}
+#endif /* HAVE_GSSAPI */
+
+
+/*
+ * 'get_md5_password()' - Get an MD5 password.
+ */
+
+static char *                          /* O - MD5 password string */
+get_md5_password(const char *username, /* I - Username */
+                 const char *group,    /* I - Group */
+                 char       passwd[33])        /* O - MD5 password string */
+{
+  cups_file_t  *fp;                    /* passwd.md5 file */
+  char         filename[1024],         /* passwd.md5 filename */
+               line[256],              /* Line from file */
+               tempuser[33],           /* User from file */
+               tempgroup[33];          /* Group from file */
+
+
+  cupsdLogMessage(CUPSD_LOG_DEBUG2,
+                  "get_md5_password(username=\"%s\", group=\"%s\", passwd=%p)",
+                  username, group ? group : "(null)", passwd);
+
+  snprintf(filename, sizeof(filename), "%s/passwd.md5", ServerRoot);
+  if ((fp = cupsFileOpen(filename, "r")) == NULL)
+  {
+    if (errno != ENOENT)
+      cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to open %s - %s", filename,
+                      strerror(errno));
+
+    return (NULL);
+  }
+
+  while (cupsFileGets(fp, line, sizeof(line)) != NULL)
+  {
+    if (sscanf(line, "%32[^:]:%32[^:]:%32s", tempuser, tempgroup, passwd) != 3)
+    {
+      cupsdLogMessage(CUPSD_LOG_ERROR, "Bad MD5 password line: %s", line);
+      continue;
+    }
+
+    if (!strcmp(username, tempuser) &&
+        (group == NULL || !strcmp(group, tempgroup)))
+    {
+     /*
+      * Found the password entry!
+      */
+
+      cupsdLogMessage(CUPSD_LOG_DEBUG2, "Found MD5 user %s, group %s...",
+                      username, tempgroup);
+
+      cupsFileClose(fp);
+      return (passwd);
+    }
+  }
+
+ /*
+  * Didn't find a password entry - return NULL!
+  */
+
+  cupsFileClose(fp);
+  return (NULL);
+}
 
 
 #if HAVE_LIBPAM
@@ -2088,7 +2644,7 @@ pam_func(
 
   return (PAM_SUCCESS);
 }
-#else
+#elif !defined(HAVE_USERSEC_H)
 
 
 /*
@@ -2112,5 +2668,5 @@ to64(char          *s,                    /* O - Output string */
 
 
 /*
- * End of "$Id: auth.c 4906 2006-01-10 20:53:28Z mike $".
+ * End of "$Id: auth.c 6947 2007-09-12 21:09:49Z mike $".
  */