]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - cups/auth.c
Load cups into easysw/current.
[thirdparty/cups.git] / cups / auth.c
index fc87d2ba5b547b495bbd2019e2ac6a439cd01284..9b67b0ee290742e841bc866d1315a31ff2838c7d 100644 (file)
@@ -1,10 +1,13 @@
 /*
- * "$Id: auth.c 6191 2007-01-10 16:48:37Z mike $"
+ * "$Id: auth.c 6253 2007-02-10 18:48:40Z mike $"
  *
  *   Authentication functions for the Common UNIX Printing System (CUPS).
  *
  *   Copyright 1997-2007 by Easy Software Products.
  *
+ *   This file contains Kerberos support code, copyright 2006 by
+ *   Jelmer Vernooij.
+ *
  *   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
@@ -26,6 +29,8 @@
  * Contents:
  *
  *   cupsDoAuthentication() - Authenticate a request.
+ *   DEBUG_gss_printf()     - Show debug error messages from GSSAPI...
+ *   cups_get_gss_creds()   - Get CUPS service credentials for authentication.
  *   cups_local_auth()      - Get the local authorization certificate if
  *                            available/applicable...
  */
 #  include <unistd.h>
 #endif /* WIN32 || __EMX__ */
 
+#if HAVE_AUTHORIZATION_H
+#  include <Security/Authorization.h>
+#  ifdef HAVE_SECBASEPRIV_H
+#    include <Security/SecBasePriv.h>
+#  else
+extern const char *cssmErrorString(int error);
+#  endif /* HAVE_SECBASEPRIV_H */
+#endif /* HAVE_AUTHORIZATION_H */
+
 
 /*
  * Local functions...
  */
 
+#ifdef HAVE_GSSAPI
+#  ifdef DEBUG
+static void    DEBUG_gss_printf(OM_uint32 major_status, OM_uint32 minor_status,
+                                const char *message);
+#  endif /* DEBUG  */
+static gss_name_t cups_get_gss_creds(http_t *http, const char *service_name);
+#endif /* HAVE_GSSAPI */
 static int     cups_local_auth(http_t *http);
 
 
@@ -74,7 +95,8 @@ cupsDoAuthentication(http_t     *http,        /* I - HTTP connection to server */
   char         prompt[1024],           /* Prompt for user */
                realm[HTTP_MAX_VALUE],  /* realm="xyz" string */
                nonce[HTTP_MAX_VALUE],  /* nonce="xyz" string */
-               encode[512];            /* Encoded username:password */
+               encode[2048];           /* Encoded username:password */
+  int          localauth;              /* Local authentication result */
   _cups_globals_t *cg;                 /* Global data */
 
 
@@ -89,27 +111,39 @@ cupsDoAuthentication(http_t     *http,     /* I - HTTP connection to server */
   * Clear the current authentication string...
   */
 
-  http->authstring[0] = '\0';
+  http->_authstring[0] = '\0';
+
+  if (http->authstring && http->authstring != http->_authstring)
+    free(http->authstring);
+
+  http->authstring = http->_authstring;
 
  /*
   * See if we can do local authentication...
   */
 
-  if (http->digest_tries < 3 && !cups_local_auth(http))
+  if (http->digest_tries < 3)
   {
-    DEBUG_printf(("cupsDoAuthentication: authstring=\"%s\"\n", http->authstring));
-
-    if (http->status == HTTP_UNAUTHORIZED)
-      http->digest_tries ++;
-
-    return (0);
+    if ((localauth = cups_local_auth(http)) == 0)
+    {
+      DEBUG_printf(("cupsDoAuthentication: authstring=\"%s\"\n",
+                    http->authstring));
+  
+      if (http->status == HTTP_UNAUTHORIZED)
+       http->digest_tries ++;
+  
+      return (0);
+    }
+    else if (localauth == -1)
+      return (-1);                     /* Error or canceled */
   }
 
  /*
   * Nope, see if we should retry the current username:password...
   */
 
-  if (http->digest_tries > 1 || !http->userpass[0])
+  if ((http->digest_tries > 1 || !http->userpass[0]) &&
+      strncmp(http->fields[HTTP_FIELD_WWW_AUTHENTICATE], "Negotiate", 9))
   {
    /*
     * Nope - get a new password from the user...
@@ -145,7 +179,106 @@ cupsDoAuthentication(http_t     *http,    /* I - HTTP connection to server */
   * Got a password; encode it for the server...
   */
 
-  if (strncmp(http->fields[HTTP_FIELD_WWW_AUTHENTICATE], "Digest", 6))
+  if (!strncmp(http->fields[HTTP_FIELD_WWW_AUTHENTICATE], "Negotiate", 9))
+  {
+#ifdef HAVE_GSSAPI
+   /*
+    * Kerberos authentication...
+    */
+
+    OM_uint32          minor_status,   /* Minor status code */
+                       major_status;   /* Major status code */
+    gss_buffer_desc    output_token = GSS_C_EMPTY_BUFFER,
+                                       /* Output token */
+                       input_token = GSS_C_EMPTY_BUFFER;
+                                       /* Input token */
+    char               *gss_service_name;
+                                       /* GSS service name */
+    const char         *authorization;
+                                       /* Pointer into Authorization string */
+
+
+    if (http->gssname == GSS_C_NO_NAME)
+    {
+      if ((gss_service_name = getenv("CUPS_GSSSERVICENAME")) == NULL)
+       gss_service_name = CUPS_DEFAULT_GSSSERVICENAME;
+      else
+       DEBUG_puts("cupsDoAuthentication: GSS service name set via environment");
+
+      http->gssname = cups_get_gss_creds(http, gss_service_name);
+    }
+
+   /*
+    * Find the start of the Kerberos input token...
+    */
+
+    authorization = httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE);
+
+    authorization += 9;
+    while (*authorization && isspace(*authorization & 255))
+      authorization ++;
+
+    if (*authorization)
+    {
+     /*
+      * For SPNEGO, this is where we'll feed the server's authorization data
+      * back into gss via input_token...
+      */
+    }
+    else
+    {
+      if (http->gssctx != GSS_C_NO_CONTEXT)
+      {
+       major_status = gss_delete_sec_context(&minor_status, &http->gssctx,
+                                             GSS_C_NO_BUFFER);
+       http->gssctx = GSS_C_NO_CONTEXT;
+      }
+    }
+
+    major_status  = gss_init_sec_context(&minor_status, GSS_C_NO_CREDENTIAL,
+                                        &http->gssctx,
+                                        http->gssname, http->gssmech,
+                                        GSS_C_MUTUAL_FLAG, GSS_C_INDEFINITE,
+                                        GSS_C_NO_CHANNEL_BINDINGS,
+                                        &input_token, &http->gssmech,
+                                        &output_token, NULL, NULL);
+
+    if (input_token.value)
+      free(input_token.value);
+
+    if (GSS_ERROR(major_status))
+    {
+#  ifdef DEBUG
+      DEBUG_gss_printf(major_status, minor_status,
+                      "Unable to initialise security context");
+#  endif /* DEBUG */
+      return (-1);
+    }
+
+#  ifdef DEBUG
+    if (major_status == GSS_S_CONTINUE_NEEDED)
+      DEBUG_gss_printf(major_status, minor_status, "Continuation needed!");
+#  endif /* DEBUG */
+
+    if (output_token.length)
+    {
+      httpEncode64_2(encode, sizeof(encode), output_token.value,
+                    output_token.length);
+
+      http->authstring = malloc(strlen(encode) + 11);
+      sprintf(http->authstring, "Negotiate %s", encode); /* Safe because allocated */
+      major_status = gss_release_buffer(&minor_status, &output_token);
+    }
+
+   /*
+    * Copy back what we can to _authstring for backwards compatibility...
+    */
+
+    strlcpy(http->_authstring, http->authstring, sizeof(http->_authstring));
+#endif /* HAVE_GSSAPI */
+  }
+  else if (strncmp(http->fields[HTTP_FIELD_WWW_AUTHENTICATE], "Digest", 6))
   {
    /*
     * Basic authentication...
@@ -153,7 +286,7 @@ cupsDoAuthentication(http_t     *http,      /* I - HTTP connection to server */
 
     httpEncode64_2(encode, sizeof(encode), http->userpass,
                    (int)strlen(http->userpass));
-    snprintf(http->authstring, sizeof(http->authstring), "Basic %s", encode);
+    snprintf(http->_authstring, sizeof(http->_authstring), "Basic %s", encode);
   }
   else
   {
@@ -166,7 +299,7 @@ cupsDoAuthentication(http_t     *http,      /* I - HTTP connection to server */
 
     httpMD5(cupsUser(), realm, strchr(http->userpass, ':') + 1, encode);
     httpMD5Final(nonce, method, resource, encode);
-    snprintf(http->authstring, sizeof(http->authstring),
+    snprintf(http->_authstring, sizeof(http->_authstring),
             "Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", "
             "uri=\"%s\", response=\"%s\"", cupsUser(), realm, nonce,
             resource, encode);
@@ -178,12 +311,105 @@ cupsDoAuthentication(http_t     *http,   /* I - HTTP connection to server */
 }
 
 
+#ifdef HAVE_GSSAPI
+#  ifdef DEBUG
+/*
+ * 'DEBUG_gss_printf()' - Show debug error messages from GSSAPI...
+ */
+
+static void
+DEBUG_gss_printf(OM_uint32 major_status,/* I - Major status code */
+                 OM_uint32 minor_status,/* I - Minor status code */
+                const char *message)   /* I - Prefix for error message */
+{
+  OM_uint32    err_major_status,       /* Major status code for display */
+               err_minor_status;       /* Minor status code for display */
+  OM_uint32    msg_ctx;                /* Message context */
+  gss_buffer_desc major_status_string = GSS_C_EMPTY_BUFFER,
+                                       /* Major status message */
+               minor_status_string = GSS_C_EMPTY_BUFFER;
+                                       /* Minor status message */
+
+
+  msg_ctx          = 0;
+  err_major_status = gss_display_status(&err_minor_status,
+                                       major_status,
+                                       GSS_C_GSS_CODE,
+                                       GSS_C_NO_OID,
+                                       &msg_ctx,
+                                       &major_status_string);
+
+  if (!GSS_ERROR(err_major_status))
+    err_major_status = gss_display_status(&err_minor_status,
+                                         minor_status,
+                                         GSS_C_MECH_CODE,
+                                         GSS_C_NULL_OID,
+                                         &msg_ctx,
+                                         &minor_status_string);
+
+  printf("%s: %s, %s\n", message, (char *)major_status_string.value,
+        (char *)minor_status_string.value);
+
+  gss_release_buffer(&err_minor_status, &major_status_string);
+  gss_release_buffer(&err_minor_status, &minor_status_string);
+}
+#  endif /* DEBUG */
+
+
+/*
+ * 'cups_get_gss_creds()' - Get CUPS service credentials for authentication.
+ */
+
+static gss_name_t                      /* O - Server name */
+cups_get_gss_creds(
+    http_t     *http,                  /* I - Connection to server */
+    const char *service_name)          /* I - Service name */
+{
+  gss_buffer_desc token = GSS_C_EMPTY_BUFFER;
+                                       /* Service token */
+  OM_uint32    major_status,           /* Major status code */
+               minor_status;           /* Minor status code */
+  gss_name_t   server_name;            /* Server name */
+  char         buf[1024],              /* Name buffer */
+               fqdn[HTTP_MAX_URI];     /* Server name buffer */
+
+
+ /*
+  * Get a server name we can use for authentication purposes...
+  */
+
+  snprintf(buf, sizeof(buf), "%s@%s", service_name,
+          httpGetHostname(http, 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);
+
+  if (GSS_ERROR(major_status))
+  {
+#  ifdef DEBUG
+    DEBUG_gss_printf(major_status, minor_status, "gss_import_name() failed");
+#  endif /* DEBUG */
+
+    return (NULL);
+  }
+
+  return (server_name);
+}
+#endif /* HAVE_GSSAPI */
+
+
 /*
  * 'cups_local_auth()' - Get the local authorization certificate if
  *                       available/applicable...
  */
 
-static int                             /* O - 0 if available, -1 if not */
+static int                             /* O - 0 if available */
+                                       /*     1 if not  available */
+                                       /*    -1 error */
 cups_local_auth(http_t *http)          /* I - HTTP connection to server */
 {
 #if defined(WIN32) || defined(__EMX__)
@@ -193,11 +419,20 @@ cups_local_auth(http_t *http)             /* I - HTTP connection to server */
 
   return (-1);
 #else
-  int          pid;                    /* Current process ID */
-  FILE         *fp;                    /* Certificate file */
-  char         filename[1024],         /* Certificate filename */
-               certificate[33];        /* Certificate string */
+  int                  pid;            /* Current process ID */
+  FILE                 *fp;            /* Certificate file */
+  char                 filename[1024], /* Certificate filename */
+                       certificate[33];/* Certificate string */
   _cups_globals_t *cg = _cupsGlobals();        /* Global data */
+#if defined(HAVE_AUTHORIZATION_H)
+  OSStatus             status;         /* Status */
+  AuthorizationItem    auth_right;     /* Authorization right */
+  AuthorizationRights  auth_rights;    /* Authorization rights */
+  AuthorizationFlags   auth_flags;     /* Authorization flags */
+  AuthorizationExternalForm auth_extrn;        /* Authorization ref external */
+  char                 auth_key[1024]; /* Buffer */
+  char                 buffer[1024];   /* Buffer */
+#endif /* HAVE_AUTHORIZATION_H */
 
 
   DEBUG_printf(("cups_local_auth(http=%p) hostaddr=%s, hostname=\"%s\"\n",
@@ -214,6 +449,79 @@ cups_local_auth(http_t *http)              /* I - HTTP connection to server */
     return (-1);
   }
 
+#if defined(HAVE_AUTHORIZATION_H)
+ /*
+  * Delete any previous authorization reference...
+  */
+  
+  if (cg->auth_ref)
+  {
+    AuthorizationFree(cg->auth_ref, kAuthorizationFlagDefaults);
+    cg->auth_ref = NULL;
+  }
+
+  if (httpGetSubField2(http, HTTP_FIELD_WWW_AUTHENTICATE, "authkey", 
+                      auth_key, sizeof(auth_key)))
+  {
+    status = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, 
+                                kAuthorizationFlagDefaults, &cg->auth_ref);
+    if (status != errAuthorizationSuccess)
+    {
+      DEBUG_printf(("cups_local_auth: AuthorizationCreate() returned %d (%s)\n",
+                   (int)status, cssmErrorString(status)));
+      return (-1);
+    }
+
+    auth_right.name        = auth_key;
+    auth_right.valueLength = 0;
+    auth_right.value       = NULL;
+    auth_right.flags       = 0;
+
+    auth_rights.count = 1;
+    auth_rights.items = &auth_right;
+
+    auth_flags = kAuthorizationFlagDefaults | 
+                kAuthorizationFlagPreAuthorize |
+                kAuthorizationFlagInteractionAllowed | 
+                kAuthorizationFlagExtendRights;
+
+    status = AuthorizationCopyRights(cg->auth_ref, &auth_rights, 
+                                    kAuthorizationEmptyEnvironment, 
+                                    auth_flags, NULL);
+    if (status == errAuthorizationSuccess)
+      status = AuthorizationMakeExternalForm(cg->auth_ref, &auth_extrn);
+
+    if (status == errAuthorizationSuccess)
+    {
+     /*
+      * Set the authorization string and return...
+      */
+
+      httpEncode64_2(buffer, sizeof(buffer), (void *)&auth_extrn, 
+                    sizeof(auth_extrn));
+
+      http->authstring = malloc(strlen(buffer) + 9);
+      sprintf(http->authstring, "AuthRef %s", buffer);
+
+      /* Copy back to _authstring for backwards compatibility */
+      strlcpy(http->_authstring, http->authstring, sizeof(http->_authstring));
+
+      DEBUG_printf(("cups_local_auth: Returning authstring = \"%s\"\n",
+                   http->authstring));
+      return (0);
+    }
+    else if (status == errAuthorizationCanceled)
+      return (-1);
+
+    DEBUG_printf(("cups_local_auth: AuthorizationCopyRights() returned %d (%s)\n",
+                 (int)status, cssmErrorString(status)));
+
+  /*
+   * Fall through to try certificates...
+   */
+  }
+#endif /* HAVE_AUTHORIZATION_H */
+
  /*
   * Try opening a certificate file for this PID.  If that fails,
   * try the root certificate...
@@ -230,34 +538,36 @@ cups_local_auth(http_t *http)             /* I - HTTP connection to server */
     fp = fopen(filename, "r");
   }
 
-  if (fp == NULL)
+  if (fp)
   {
-    DEBUG_printf(("cups_local_auth: Unable to open file %s: %s\n",
-                  filename, strerror(errno)));
-    return (-1);
-  }
+   /*
+    * Read the certificate from the file...
+    */
 
- /*
-  * Read the certificate from the file...
-  */
+    fgets(certificate, sizeof(certificate), fp);
+    fclose(fp);
 
-  fgets(certificate, sizeof(certificate), fp);
-  fclose(fp);
+   /*
+    * Set the authorization string and return...
+    */
 
- /*
-  * Set the authorization string and return...
-  */
+    http->authstring = malloc(strlen(certificate) + 10);
+    sprintf(http->authstring, "Local %s", certificate);
 
-  snprintf(http->authstring, sizeof(http->authstring), "Local %s", certificate);
+    /* Copy back to _authstring for backwards compatibility */
+    strlcpy(http->_authstring, http->authstring, sizeof(http->_authstring));
 
-  DEBUG_printf(("cups_local_auth: Returning authstring = \"%s\"\n",
-                http->authstring));
+    DEBUG_printf(("cups_local_auth: Returning authstring = \"%s\"\n",
+                 http->authstring));
 
-  return (0);
+    return (0);
+  }
+
+  return (1);
 #endif /* WIN32 || __EMX__ */
 }
 
 
 /*
- * End of "$Id: auth.c 6191 2007-01-10 16:48:37Z mike $".
+ * End of "$Id: auth.c 6253 2007-02-10 18:48:40Z mike $".
  */