]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - scheduler/client.c
Load cups into easysw/current.
[thirdparty/cups.git] / scheduler / client.c
index 8a338d412224a365e7980a47e16f92a3f0e19c79..44a76f49e9a9e614c42036121730691da7e0305b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * "$Id: client.c 6111 2006-11-15 20:28:39Z mike $"
+ * "$Id: client.c 6247 2007-02-07 20:54:37Z mike $"
  *
  *   Client routines for the Common UNIX Printing System (CUPS) scheduler.
  *
@@ -35,7 +35,7 @@
  *   cupsdWriteClient()      - Write data to a client as needed.
  *   check_if_modified()     - Decode an "If-Modified-Since" line.
  *   encrypt_client()        - Enable encryption for the client...
- *   get_cdsa_server_certs() - Convert a keychain name into the CFArrayRef
+ *   get_cdsa_certificate()  - Convert a keychain name into the CFArrayRef
  *                            required by SSLSetCertificate.
  *   get_file()              - Get a filename and state info.
  *   install_conf_file()     - Install a configuration file.
 
 #ifdef HAVE_CDSASSL
 #  include <Security/Security.h>
+#  ifdef HAVE_SECIDENTITYSEARCHPRIV_H
+#    include <Security/SecIdentitySearchPriv.h>
+#  else /* Declare prototype for function in that header... */
+extern OSStatus SecIdentitySearchCreateWithPolicy(SecPolicyRef policy, 
+                               CFStringRef idString, CSSM_KEYUSE keyUsage, 
+                               CFTypeRef keychainOrArray, 
+                               Boolean returnOnlyValidIdentities, 
+                               SecIdentitySearchRef* searchRef);
+#  endif /* HAVE_SECIDENTITYSEARCHPRIV_H */
+#  ifdef HAVE_SECPOLICYPRIV_H
+#    include <Security/SecPolicyPriv.h>
+#  else /* Declare prototype for function in that header... */
+extern OSStatus SecPolicySetValue(SecPolicyRef policyRef, 
+                                  const CSSM_DATA *value);
+#  endif /* HAVE_SECPOLICYPRIV_H */
 #  ifdef HAVE_SECBASEPRIV_H
 #    include <Security/SecBasePriv.h>
-#  else
-     extern const char *cssmErrorString(int error);
+#  else /* Declare prototype for function in that header... */
+extern const char *cssmErrorString(int error);
 #  endif /* HAVE_SECBASEPRIV_H */
 #endif /* HAVE_CDSASSL */
+
 #ifdef HAVE_GNUTLS
 #  include <gnutls/x509.h>
 #endif /* HAVE_GNUTLS */
@@ -76,7 +92,7 @@ static int            check_if_modified(cupsd_client_t *con,
 static int             encrypt_client(cupsd_client_t *con);
 #endif /* HAVE_SSL */
 #ifdef HAVE_CDSASSL
-static CFArrayRef      get_cdsa_server_certs(void);
+static CFArrayRef      get_cdsa_certificate(cupsd_client_t *con);
 #endif /* HAVE_CDSASSL */
 static char            *get_file(cupsd_client_t *con, struct stat *filestats, 
                                  char *filename, int len);
@@ -85,7 +101,7 @@ static int           is_cgi(cupsd_client_t *con, const char *filename,
                               struct stat *filestats, mime_type_t *type);
 static int             is_path_absolute(const char *path);
 #ifdef HAVE_SSL
-static int             make_certificate(void);
+static int             make_certificate(cupsd_client_t *con);
 #endif /* HAVE_SSL */
 static int             pipe_command(cupsd_client_t *con, int infile, int *outfile,
                                     char *command, char *options, int root);
@@ -635,6 +651,7 @@ cupsdCloseClient(cupsd_client_t *con)       /* I - Client to close */
     cupsdClearString(&con->filename);
     cupsdClearString(&con->command);
     cupsdClearString(&con->options);
+    cupsdClearString(&con->query_string);
 
     if (con->request)
     {
@@ -798,6 +815,7 @@ cupsdReadClient(cupsd_client_t *con)        /* I - Client to read from */
 
        cupsdClearString(&con->command);
        cupsdClearString(&con->options);
+       cupsdClearString(&con->query_string);
 
        if (con->request)
        {
@@ -2087,7 +2105,8 @@ cupsdSendError(cupsd_client_t *con,       /* I - Connection */
               con->servername, con->serverport, con->uri);
 
       snprintf(redirect, sizeof(redirect),
-               "<META HTTP-EQUIV=\"Refresh\" CONTENT=\"3;https://%s:%d%s\">\n",
+               "<META HTTP-EQUIV=\"Refresh\" "
+              "CONTENT=\"3;URL=https://%s:%d%s\">\n",
               con->servername, con->serverport, con->uri);
     }
     else
@@ -2494,6 +2513,7 @@ cupsdWriteClient(cupsd_client_t *con)     /* I - Client connection */
 
     cupsdClearString(&con->command);
     cupsdClearString(&con->options);
+    cupsdClearString(&con->query_string);
 
     if (!con->http.keep_alive)
     {
@@ -2606,7 +2626,7 @@ encrypt_client(cupsd_client_t *con)       /* I - Client to encrypt */
     * Nope, make a self-signed certificate...
     */
 
-    if (!make_certificate())
+    if (!make_certificate(con))
       return (0);
   }
 
@@ -2665,7 +2685,7 @@ encrypt_client(cupsd_client_t *con)       /* I - Client to encrypt */
     * Nope, make a self-signed certificate...
     */
 
-    if (!make_certificate())
+    if (!make_certificate(con))
       return (0);
   }
 
@@ -2737,7 +2757,7 @@ encrypt_client(cupsd_client_t *con)       /* I - Client to encrypt */
 
   error            = 0;
   conn->session    = NULL;
-  conn->certsArray = get_cdsa_server_certs();
+  conn->certsArray = get_cdsa_certificate(con);
 
   if (!conn->certsArray)
   {
@@ -2745,8 +2765,8 @@ encrypt_client(cupsd_client_t *con)       /* I - Client to encrypt */
     * No keychain (yet), make a self-signed certificate...
     */
 
-    if (make_certificate())
-      conn->certsArray = get_cdsa_server_certs();
+    if (make_certificate(con))
+      conn->certsArray = get_cdsa_certificate(con);
   }
 
   if (!conn->certsArray)
@@ -2825,90 +2845,120 @@ encrypt_client(cupsd_client_t *con)    /* I - Client to encrypt */
 
 #ifdef HAVE_CDSASSL
 /*
- * 'get_cdsa_server_certs()' - Convert a keychain name into the CFArrayRef
- *                            required by SSLSetCertificate.
- *
- * For now we assumes that there is exactly one SecIdentity in the
- * keychain - i.e. there is exactly one matching cert/private key pair.
- * In the future we will search a keychain for a SecIdentity matching a
- * specific criteria.  We also skip the operation of adding additional
- * non-signing certs from the keychain to the CFArrayRef.
- *
- * To create a self-signed certificate for testing use the certtool.
- * Executing the following as root will do it:
- *
- *     certtool c k=/Library/Keychains/System.keychain
+ * 'get_cdsa_certificate()' - Get a SSL/TLS certificate from the System keychain.
  */
 
-static CFArrayRef                      /* O - Array of certificates */
-get_cdsa_server_certs(void)
+static CFArrayRef                              /* O - Array of certificates */
+get_cdsa_certificate(cupsd_client_t *con)      /* I - Client connection */
 {
   OSStatus             err;            /* Error info */
-  SecKeychainRef       kcRef;          /* Keychain reference */
-  SecIdentitySearchRef srchRef;        /* Search reference */
+  SecKeychainRef       keychain;       /* Keychain reference */
+  SecIdentitySearchRef search;         /* Search reference */
   SecIdentityRef       identity;       /* Identity */
-  CFArrayRef           ca;             /* Certificate array */
+  CFArrayRef           certificates = NULL;
+                                       /* Certificate array */
 
 
-  kcRef    = NULL;
-  srchRef  = NULL;
-  identity = NULL;
-  ca      = NULL;
-  err      = SecKeychainOpen(ServerCertificate, &kcRef);
+  if ((err = SecKeychainOpen(ServerCertificate, &keychain)))
+  {
+    cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot open keychain \"%s\", %s",
+                   ServerCertificate, cssmErrorString(err));
+    return (NULL);
+  }
 
-  if (err)
-    cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot open keychain \"%s\", error %d.",
-                   ServerCertificate, (int)err);
-  else
+#  if HAVE_SECIDENTITYSEARCHCREATEWITHPOLICY
+ /*
+  * Use a policy to search for valid certificates who's common name matches the
+  * servername...
+  */
+
+  SecPolicySearchRef   policy_search;  /* Policy search ref */
+  SecPolicyRef         policy;         /* Policy ref */
+  CSSM_DATA            options;        /* Policy options */
+  CSSM_APPLE_TP_SSL_OPTIONS
+                       ssl_options;    /* SSL Option for hostname */
+
+
+  if ((err = SecPolicySearchCreate(CSSM_CERT_X_509v3, &CSSMOID_APPLE_TP_SSL, 
+                             NULL, &policy_search)))
   {
-   /*
-    * Search for "any" identity matching specified key use; 
-    * in this app, we expect there to be exactly one. 
-    */
+    cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create a policy search reference");
+    CFRelease(keychain);
+    return (NULL);
+  }
 
-    err = SecIdentitySearchCreate(kcRef, CSSM_KEYUSE_SIGN, &srchRef);
+  if ((err = SecPolicySearchCopyNext(policy_search, &policy)))
+  {
+    cupsdLogMessage(CUPSD_LOG_ERROR, 
+                   "Cannot find a policy to use for searching");
+    CFRelease(keychain);
+    CFRelease(policy_search);
+    return (NULL);
+  }
 
-    if (err)
-      cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                     "Cannot find signing key in keychain \"%s\", error %d",
-                     ServerCertificate, (int)err);
-    else
-    {
-      err = SecIdentitySearchCopyNext(srchRef, &identity);
+  memset(&ssl_options, 0, sizeof(ssl_options));
+  ssl_options.Version = CSSM_APPLE_TP_SSL_OPTS_VERSION;
+  ssl_options.ServerName = con->servername;
+  ssl_options.ServerNameLen = strlen(con->servername);
 
-      if (err)
-       cupsdLogMessage(CUPSD_LOG_DEBUG2,
+  options.Data = (uint8 *)&ssl_options;
+  options.Length = sizeof(ssl_options);
+
+  if ((err = SecPolicySetValue(policy, &options)))
+  {
+    cupsdLogMessage(CUPSD_LOG_ERROR, 
+                   "Cannot set policy value to use for searching");
+    CFRelease(keychain);
+    CFRelease(policy_search);
+    return (NULL);
+  }
+
+  err = SecIdentitySearchCreateWithPolicy(policy, NULL, CSSM_KEYUSE_SIGN,
+                                         keychain, FALSE, &search);
+#  else
+ /*
+  * Assume there is exactly one SecIdentity in the keychain...
+  */
+
+  err = SecIdentitySearchCreate(keychain, CSSM_KEYUSE_SIGN, &search);
+#  endif /* HAVE_SECIDENTITYSEARCHCREATEWITHPOLICY */
+
+  if (err)
+    cupsdLogMessage(CUPSD_LOG_DEBUG,
+                   "Cannot create keychain search reference: %s", 
+                   cssmErrorString(err));
+  else
+  {
+    if ((err = SecIdentitySearchCopyNext(search, &identity)))
+    {
+      cupsdLogMessage(CUPSD_LOG_DEBUG,
                        "Cannot find signing key in keychain \"%s\", error %d",
                        ServerCertificate, (int)err);
+    }
+    else
+    {
+      if (CFGetTypeID(identity) != SecIdentityGetTypeID())
+       cupsdLogMessage(CUPSD_LOG_ERROR,
+                       "SecIdentitySearchCopyNext CFTypeID failure!");
       else
       {
-       if (CFGetTypeID(identity) != SecIdentityGetTypeID())
-         cupsdLogMessage(CUPSD_LOG_ERROR,
-                         "SecIdentitySearchCopyNext CFTypeID failure!");
-       else
-       {
-        /* 
-         * Found one. Place it in a CFArray. 
-         * TBD: snag other (non-identity) certs from keychain and add them
-         * to array as well.
-         */
-
-         ca = CFArrayCreate(NULL, (const void **)&identity, 1, &kCFTypeArrayCallBacks);
-
-         if (ca == nil)
-           cupsdLogMessage(CUPSD_LOG_ERROR, "CFArrayCreate error");
-       }
-
-       CFRelease(identity);
+      if ((certificates = CFArrayCreate(NULL, (const void **)&identity, 
+                                     1, &kCFTypeArrayCallBacks)) == NULL)
+       cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create certificate array");
       }
 
-      CFRelease(srchRef);
+      CFRelease(identity);
     }
 
-    CFRelease(kcRef);
+    CFRelease(search);
   }
 
-  return (ca);
+#  if HAVE_SECIDENTITYSEARCHCREATEWITHPOLICY
+  CFRelease(policy);
+  CFRelease(policy_search);
+#  endif /* HAVE_SECIDENTITYSEARCHCREATEWITHPOLICY */
+
+  return (certificates);
 }
 #endif /* HAVE_CDSASSL */
 
@@ -3248,8 +3298,13 @@ is_cgi(cupsd_client_t *con,              /* I - Client connection */
   */
 
   if ((options = strchr(con->uri, '?')) != NULL)
+  {
     options ++;
 
+    if (strchr(options, '='))
+      cupsdSetStringf(&(con->query_string), "QUERY_STRING=%s", options);
+  }
+
  /*
   * Check for known types...
   */
@@ -3411,7 +3466,7 @@ is_path_absolute(const char *path)        /* I - Input path */
  */
 
 static int                             /* O - 1 on success, 0 on failure */
-make_certificate(void)
+make_certificate(cupsd_client_t *con)  /* I - Client connection */
 {
 #if defined(HAVE_LIBSSL) && defined(HAVE_WAITPID)
   int          pid,                    /* Process ID of command */
@@ -3748,18 +3803,19 @@ make_certificate(void)
   return (1);
 
 #elif defined(HAVE_CDSASSL) && defined(HAVE_WAITPID)
-  int  pid,                            /* Process ID of command */
-       status;                         /* Status of command */
-  char command[1024],                  /* Command */
-       keychain[1024],                 /* Keychain argument */
-       *argv[5],                       /* Command-line arguments */
-       *envp[MAX_ENV];                 /* Environment variables */
+  int          pid,                    /* Process ID of command */
+               status;                 /* Status of command */
+  char         command[1024],          /* Command */
+               *argv[4],               /* Command-line arguments */
+               *envp[MAX_ENV + 1],     /* Environment variables */
+               keychain[1024],         /* Keychain argument */
+               infofile[1024];         /* Type-in information for cert */
+  cups_file_t  *fp;                    /* Seed/info file */
+  int          infofd;                 /* Info file descriptor */
 
 
  /*
-  * Run the "certtool" command to generate a self-signed certificate:
-  *
-  *     certtool c Z k=ServerCertificate
+  * Run the "certtool" command to generate a self-signed certificate...
   */
 
   if (!cupsFileFind("certtool", getenv("PATH"), 1, command, sizeof(command)))
@@ -3769,6 +3825,25 @@ make_certificate(void)
     return (0);
   }
 
+ /*
+  * Create a file with the certificate information fields...
+  *
+  * Note: This assumes that the default questions are asked by the certtool
+  * command...
+  */
+
+  if ((fp = cupsTempFile2(infofile, sizeof(infofile))) == NULL)
+  {
+    cupsdLogMessage(CUPSD_LOG_ERROR,
+                    "Unable to create certificate information file %s - %s",
+                    infofile, strerror(errno));
+    return (0);
+  }
+
+  cupsFilePrintf(fp, "%s\nr\n\ny\nb\ns\ny\n%s\n\n\n\n\n%s\ny\n", 
+                con->servername, con->servername, ServerAdmin);
+  cupsFileClose(fp);
+
   cupsdLogMessage(CUPSD_LOG_INFO,
                   "Generating SSL server key and certificate...");
 
@@ -3776,14 +3851,22 @@ make_certificate(void)
 
   argv[0] = "certtool";
   argv[1] = "c";
-  argv[2] = "Z";
-  argv[3] = keychain;
-  argv[4] = NULL;
+  argv[2] = keychain;
+  argv[3] = NULL;
 
   cupsdLoadEnv(envp, MAX_ENV);
 
-  if (!cupsdStartProcess(command, argv, envp, -1, -1, -1, -1, 1, &pid))
+  infofd = open(infofile, O_RDONLY);
+
+  if (!cupsdStartProcess(command, argv, envp, infofd, -1, -1, -1, 1, &pid))
+  {
+    close(infofd);
+    unlink(infofile);
     return (0);
+  }
+
+  close(infofd);
+  unlink(infofile);
 
   while (waitpid(pid, &status, 0) < 0)
     if (errno != EINTR)
@@ -3852,7 +3935,6 @@ pipe_command(cupsd_client_t *con, /* I - Client connection */
                http_user_agent[1024],  /* HTTP_USER_AGENT environment variable */
                lang[1024],             /* LANG environment variable */
                path_info[1024],        /* PATH_INFO environment variable */
-               *query_string,          /* QUERY_STRING env variable */
                remote_addr[1024],      /* REMOTE_ADDR environment variable */
                remote_host[1024],      /* REMOTE_HOST environment variable */
                remote_user[1024],      /* REMOTE_USER environment variable */
@@ -3883,8 +3965,7 @@ pipe_command(cupsd_client_t *con, /* I - Client connection */
                   "pipe_command: command=\"%s\", options=\"%s\"",
                   command, options ? options : "(null)");
 
-  argv[0]      = command;
-  query_string = NULL;
+  argv[0] = command;
 
   if (options)
     strlcpy(argbuf, options, sizeof(argbuf));
@@ -3911,10 +3992,12 @@ pipe_command(cupsd_client_t *con,       /* I - Client connection */
     path_info[0] = '\0';
   }
 
-  if (*commptr == '?' && con->operation == HTTP_GET)
+  cupsdLogMessage(CUPSD_LOG_INFO, "commptr=\"%s\"", commptr);
+
+  if (*commptr == '?' && con->operation == HTTP_GET && !con->query_string)
   {
     commptr ++;
-    cupsdSetStringf(&query_string, "QUERY_STRING=%s", commptr);
+    cupsdSetStringf(&(con->query_string), "QUERY_STRING=%s", commptr);
   }
 
   argc = 1;
@@ -4045,18 +4128,15 @@ pipe_command(cupsd_client_t *con,       /* I - Client connection */
 
   if (con->operation == HTTP_GET)
   {
-    for (i = 0; i < argc; i ++)
-      cupsdLogMessage(CUPSD_LOG_DEBUG2, "argv[%d] = \"%s\"", i, argv[i]);
-
     envp[envc ++] = "REQUEST_METHOD=GET";
 
-    if (query_string)
+    if (con->query_string)
     {
      /*
       * Add GET form variables after ?...
       */
 
-      envp[envc ++] = query_string;
+      envp[envc ++] = con->query_string;
     }
   }
   else
@@ -4100,8 +4180,6 @@ pipe_command(cupsd_client_t *con, /* I - Client connection */
 
   if (cupsdOpenPipe(fds))
   {
-    cupsdClearString(&query_string);
-
     cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to create pipes for CGI %s - %s",
                     argv[0], strerror(errno));
     return (0);
@@ -4139,8 +4217,6 @@ pipe_command(cupsd_client_t *con, /* I - Client connection */
     close(fds[1]);
   }
 
-  cupsdClearString(&query_string);
-
   return (pid);
 }
 
@@ -4201,5 +4277,5 @@ write_file(cupsd_client_t *con,           /* I - Client connection */
 
 
 /*
- * End of "$Id: client.c 6111 2006-11-15 20:28:39Z mike $".
+ * End of "$Id: client.c 6247 2007-02-07 20:54:37Z mike $".
  */