save_auth_info(
    cupsd_client_t  *con,		/* I - Client connection */
    cupsd_job_t     *job,		/* I - Job */
    ipp_attribute_t *auth_info)		/* I - auth-info attribute, if any */
{
  int			i;		/* Looping var */
  char			filename[1024];	/* Job authentication filename */
  cups_file_t		*fp;		/* Job authentication file */
  char			line[65536];	/* Line for file */
  cupsd_printer_t	*dest;		/* Destination printer/class */


 /*
  * This function saves the in-memory authentication information for
  * a job so that it can be used to authenticate with a remote host.
  * The information is stored in a file that is readable only by the
  * root user.  The fields are Base-64 encoded, each on a separate line,
  * followed by random number (up to 1024) of newlines to limit the
  * amount of information that is exposed.
  *
  * Because of the potential for exposing of authentication information,
  * this functionality is only enabled when running cupsd as root.
  *
  * This caching only works for the Basic and BasicDigest authentication
  * types.  Digest authentication cannot be cached this way, and in
  * the future Kerberos authentication may make all of this obsolete.
  *
  * Authentication information is saved whenever an authenticated
  * Print-Job, Create-Job, or CUPS-Authenticate-Job operation is
  * performed.
  *
  * This information is deleted after a job is completed or canceled,
  * so reprints may require subsequent re-authentication.
  */

  if (RunUser)
    return;

  if ((dest = cupsdFindDest(job->dest)) == NULL)
    return;

 /*
  * Create the authentication file and change permissions...
  */

  snprintf(filename, sizeof(filename), "%s/a%05d", RequestRoot, job->id);
  if ((fp = cupsFileOpen(filename, "w")) == NULL)
  {
    cupsdLogMessage(CUPSD_LOG_ERROR,
                    "Unable to save authentication info to \"%s\" - %s",
                    filename, strerror(errno));
    return;
  }

  fchown(cupsFileNumber(fp), 0, 0);
  fchmod(cupsFileNumber(fp), 0400);

  cupsFilePuts(fp, "CUPSD-AUTH-V3\n");

  for (i = 0;
       i < (int)(sizeof(job->auth_env) / sizeof(job->auth_env[0]));
       i ++)
    cupsdClearString(job->auth_env + i);

  if (auth_info && auth_info->num_values == dest->num_auth_info_required)
  {
   /*
    * Write 1 to 3 auth values...
    */

    for (i = 0;
         i < auth_info->num_values &&
	     i < (int)(sizeof(job->auth_env) / sizeof(job->auth_env[0]));
	 i ++)
    {
      if (strcmp(dest->auth_info_required[i], "negotiate"))
      {
	httpEncode64_2(line, sizeof(line), auth_info->values[i].string.text, (int)strlen(auth_info->values[i].string.text));
	cupsFilePutConf(fp, dest->auth_info_required[i], line);
      }
      else
	cupsFilePutConf(fp, dest->auth_info_required[i],
	                auth_info->values[i].string.text);

      if (!strcmp(dest->auth_info_required[i], "username"))
        cupsdSetStringf(job->auth_env + i, "AUTH_USERNAME=%s",
	                auth_info->values[i].string.text);
      else if (!strcmp(dest->auth_info_required[i], "domain"))
        cupsdSetStringf(job->auth_env + i, "AUTH_DOMAIN=%s",
	                auth_info->values[i].string.text);
      else if (!strcmp(dest->auth_info_required[i], "password"))
        cupsdSetStringf(job->auth_env + i, "AUTH_PASSWORD=%s",
	                auth_info->values[i].string.text);
      else if (!strcmp(dest->auth_info_required[i], "negotiate"))
        cupsdSetStringf(job->auth_env + i, "AUTH_NEGOTIATE=%s",
	                auth_info->values[i].string.text);
      else
        i --;
    }
  }
  else if (auth_info && auth_info->num_values == 2 &&
           dest->num_auth_info_required == 1 &&
           !strcmp(dest->auth_info_required[0], "negotiate"))
  {
   /*
    * Allow fallback to username+password for Kerberized queues...
    */

    httpEncode64_2(line, sizeof(line), auth_info->values[0].string.text, (int)strlen(auth_info->values[0].string.text));
    cupsFilePutConf(fp, "username", line);

    cupsdSetStringf(job->auth_env + 0, "AUTH_USERNAME=%s",
                    auth_info->values[0].string.text);

    httpEncode64_2(line, sizeof(line), auth_info->values[1].string.text, (int)strlen(auth_info->values[1].string.text));
    cupsFilePutConf(fp, "password", line);

    cupsdSetStringf(job->auth_env + 1, "AUTH_PASSWORD=%s",
                    auth_info->values[1].string.text);
  }
  else if (con->username[0])
  {
   /*
    * Write the authenticated username...
    */

    httpEncode64_2(line, sizeof(line), con->username, (int)strlen(con->username));
    cupsFilePutConf(fp, "username", line);

    cupsdSetStringf(job->auth_env + 0, "AUTH_USERNAME=%s", con->username);

   /*
    * Write the authenticated password...
    */

    httpEncode64_2(line, sizeof(line), con->password, (int)strlen(con->password));
    cupsFilePutConf(fp, "password", line);

    cupsdSetStringf(job->auth_env + 1, "AUTH_PASSWORD=%s", con->password);
  }

#ifdef HAVE_GSSAPI
  if (con->gss_uid > 0)
  {
    cupsFilePrintf(fp, "uid %d\n", (int)con->gss_uid);
    cupsdSetStringf(&job->auth_uid, "AUTH_UID=%d", (int)con->gss_uid);
  }
#endif /* HAVE_GSSAPI */

 /*
  * Write a random number of newlines to the end of the file...
  */

  for (i = (CUPS_RAND() % 1024); i >= 0; i --)
    cupsFilePutChar(fp, '\n');

 /*
  * Close the file and return...
  */

  cupsFileClose(fp);
}