1 /*
2  * "$Id: auth.c 9233 2010-08-10 06:15:55Z mike $"
3  *
4  *   Authentication functions for the Common UNIX Printing System (CUPS).
5  *
6  *   Copyright 2007-2010 by Apple Inc.
7  *   Copyright 1997-2007 by Easy Software Products.
8  *
9  *   This file contains Kerberos support code, copyright 2006 by
10  *   Jelmer Vernooij.
11  *
12  *   These coded instructions, statements, and computer programs are the
13  *   property of Apple Inc. and are protected by Federal copyright
14  *   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
15  *   which should have been included with this file.  If this file is
16  *   file is missing or damaged, see the license at "http://www.cups.org/".
17  *
18  *   This file is subject to the Apple OS-Developed Software exception.
19  *
20  * Contents:
21  *
22  *   cupsDoAuthentication() - Authenticate a request.
23  *   cups_get_gssname()     - Get GSSAPI name for authentication.
24  *   cups_gss_printf()      - Show error messages from GSSAPI...
25  *   cups_local_auth()      - Get the local authorization certificate if
26  *                            available/applicable...
27  */
28 
29 /*
30  * Include necessary headers...
31  */
32 
33 #include "globals.h"
34 #include "debug.h"
35 #include <stdlib.h>
36 #include <ctype.h>
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <sys/stat.h>
40 #if defined(WIN32) || defined(__EMX__)
41 #  include <io.h>
42 #else
43 #  include <unistd.h>
44 #endif /* WIN32 || __EMX__ */
45 
46 #if HAVE_AUTHORIZATION_H
47 #  include <Security/Authorization.h>
48 #  ifdef HAVE_SECBASEPRIV_H
49 #    include <Security/SecBasePriv.h>
50 #  else
51 extern const char *cssmErrorString(int error);
52 #  endif /* HAVE_SECBASEPRIV_H */
53 #endif /* HAVE_AUTHORIZATION_H */
54 
55 #if defined(SO_PEERCRED) && defined(AF_LOCAL)
56 #  include <pwd.h>
57 #endif /* SO_PEERCRED && AF_LOCAL */
58 
59 
60 /*
61  * Local functions...
62  */
63 
64 #ifdef HAVE_GSSAPI
65 static gss_name_t cups_get_gssname(http_t *http, const char *service_name);
66 #  ifdef DEBUG
67 static void	cups_gss_printf(OM_uint32 major_status, OM_uint32 minor_status,
68 				const char *message);
69 #  else
70 #    define	cups_gss_printf(major, minor, message)
71 #  endif /* DEBUG */
72 #endif /* HAVE_GSSAPI */
73 static int	cups_local_auth(http_t *http);
74 
75 
76 /*
77  * 'cupsDoAuthentication()' - Authenticate a request.
78  *
79  * This function should be called in response to a @code HTTP_UNAUTHORIZED@
80  * status, prior to resubmitting your request.
81  *
82  * @since CUPS 1.1.20/Mac OS X 10.4@
83  */
84 
85 int					/* O - 0 on success, -1 on error */
cupsDoAuthentication(http_t * http,const char * method,const char * resource)86 cupsDoAuthentication(
87     http_t     *http,			/* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
88     const char *method,			/* I - Request method ("GET", "POST", "PUT") */
89     const char *resource)		/* I - Resource path */
90 {
91   const char	*password;		/* Password string */
92   char		prompt[1024],		/* Prompt for user */
93 		realm[HTTP_MAX_VALUE],	/* realm="xyz" string */
94 		nonce[HTTP_MAX_VALUE];	/* nonce="xyz" string */
95   int		localauth;		/* Local authentication result */
96   _cups_globals_t *cg;			/* Global data */
97 
98 
99   DEBUG_printf(("cupsDoAuthentication(http=%p, method=\"%s\", resource=\"%s\")",
100                 http, method, resource));
101 
102   if (!http)
103     http = _cupsConnect();
104 
105   if (!http || !method || !resource)
106     return (-1);
107 
108   DEBUG_printf(("2cupsDoAuthentication: digest_tries=%d, userpass=\"%s\"",
109                 http->digest_tries, http->userpass));
110   DEBUG_printf(("2cupsDoAuthentication: WWW-Authenticate=\"%s\"",
111                 httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE)));
112 
113  /*
114   * Clear the current authentication string...
115   */
116 
117   httpSetAuthString(http, NULL, NULL);
118 
119  /*
120   * See if we can do local authentication...
121   */
122 
123   if (http->digest_tries < 3)
124   {
125     if ((localauth = cups_local_auth(http)) == 0)
126     {
127       DEBUG_printf(("2cupsDoAuthentication: authstring=\"%s\"",
128                     http->authstring));
129 
130       if (http->status == HTTP_UNAUTHORIZED)
131 	http->digest_tries ++;
132 
133       return (0);
134     }
135     else if (localauth == -1)
136     {
137       http->status = HTTP_AUTHORIZATION_CANCELED;
138       return (-1);			/* Error or canceled */
139     }
140   }
141 
142  /*
143   * Nope, see if we should retry the current username:password...
144   */
145 
146   if ((http->digest_tries > 1 || !http->userpass[0]) &&
147       strncmp(http->fields[HTTP_FIELD_WWW_AUTHENTICATE], "Negotiate", 9))
148   {
149    /*
150     * Nope - get a new password from the user...
151     */
152 
153     cg = _cupsGlobals();
154 
155     if (!cg->lang_default)
156       cg->lang_default = cupsLangDefault();
157 
158     snprintf(prompt, sizeof(prompt),
159              _cupsLangString(cg->lang_default, _("Password for %s on %s? ")),
160 	     cupsUser(),
161 	     http->hostname[0] == '/' ? "localhost" : http->hostname);
162 
163     http->digest_tries  = strncasecmp(http->fields[HTTP_FIELD_WWW_AUTHENTICATE],
164                                       "Digest", 5) != 0;
165     http->userpass[0]   = '\0';
166 
167     if ((password = cupsGetPassword2(prompt, http, method, resource)) == NULL)
168     {
169       http->status = HTTP_AUTHORIZATION_CANCELED;
170       return (-1);
171     }
172 
173     if (!password[0])
174     {
175       http->status = HTTP_AUTHORIZATION_CANCELED;
176       return (-1);
177     }
178 
179     snprintf(http->userpass, sizeof(http->userpass), "%s:%s", cupsUser(),
180              password);
181   }
182   else if (http->status == HTTP_UNAUTHORIZED)
183     http->digest_tries ++;
184 
185   if (http->status == HTTP_UNAUTHORIZED && http->digest_tries >= 3)
186   {
187     DEBUG_printf(("1cupsDoAuthentication: Too many authentication tries (%d)",
188 		  http->digest_tries));
189 
190     http->status = HTTP_AUTHORIZATION_CANCELED;
191     return (-1);
192   }
193 
194  /*
195   * Got a password; encode it for the server...
196   */
197 
198   if (!strncmp(http->fields[HTTP_FIELD_WWW_AUTHENTICATE], "Negotiate", 9))
199   {
200 #ifdef HAVE_GSSAPI
201    /*
202     * Kerberos authentication...
203     */
204 
205     OM_uint32		minor_status,	/* Minor status code */
206 			major_status;	/* Major status code */
207     gss_buffer_desc	output_token = GSS_C_EMPTY_BUFFER,
208 					/* Output token */
209 			input_token = GSS_C_EMPTY_BUFFER;
210 					/* Input token */
211     char		*gss_service_name;
212 					/* GSS service name */
213 #  ifdef USE_SPNEGO
214     const char		*authorization;
215 					/* Pointer into Authorization string */
216 #  endif /* USE_SPNEGO */
217 
218 
219 #  ifdef __APPLE__
220    /*
221     * If the weak-linked GSSAPI/Kerberos library is not present, don't try
222     * to use it...
223     */
224 
225     if (gss_init_sec_context == NULL)
226     {
227       DEBUG_puts("1cupsDoAuthentication: Weak-linked GSSAPI/Kerberos framework "
228                  "is not present");
229       http->status = HTTP_AUTHORIZATION_CANCELED;
230 
231       return (-1);
232     }
233 #  endif /* __APPLE__ */
234 
235     if (http->gssname == GSS_C_NO_NAME)
236     {
237       if ((gss_service_name = getenv("CUPS_GSSSERVICENAME")) == NULL)
238 	gss_service_name = CUPS_DEFAULT_GSSSERVICENAME;
239       else
240 	DEBUG_puts("2cupsDoAuthentication: GSS service name set via "
241 	           "environment variable");
242 
243       http->gssname = cups_get_gssname(http, gss_service_name);
244     }
245 
246 #  ifdef USE_SPNEGO /* We don't implement SPNEGO just yet... */
247    /*
248     * Find the start of the Kerberos input token...
249     */
250 
251     authorization = httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE);
252 
253     authorization += 9;
254     while (*authorization && _cups_isspace(*authorization))
255       authorization ++;
256 
257     if (*authorization)
258     {
259      /*
260       * Decode the authorization string to get the input token...
261       */
262 
263       int len = strlen(authorization);
264 
265       input_token.value  = malloc(len);
266       input_token.value  = httpDecode64_2(input_token.value, &len,
267 					  authorization);
268       input_token.length = len;
269 
270 #    ifdef DEBUG
271       {
272         char *ptr = (char *)input_token.value;
273 	int left = len;
274 
275         fputs("input_token=", stdout);
276 	while (left > 0)
277 	{
278 	  if (*ptr < ' ')
279 	    printf("\\%03o", *ptr & 255);
280 	  else
281 	    putchar(*ptr);
282 	  ptr ++;
283 	  left --;
284 	}
285 	putchar('\n');
286       }
287 #    endif /* DEBUG */
288     }
289 #  endif /* USE_SPNEGO */
290 
291     if (http->gssctx != GSS_C_NO_CONTEXT)
292     {
293       gss_delete_sec_context(&minor_status, &http->gssctx, GSS_C_NO_BUFFER);
294       http->gssctx = GSS_C_NO_CONTEXT;
295     }
296 
297     major_status  = gss_init_sec_context(&minor_status, GSS_C_NO_CREDENTIAL,
298 					 &http->gssctx,
299 					 http->gssname, http->gssmech,
300 #ifdef GSS_C_DELEG_POLICY_FLAG
301 					 GSS_C_DELEG_POLICY_FLAG |
302 #endif /* GSS_C_DELEG_POLICY_FLAG */
303 					 GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG,
304 					 GSS_C_INDEFINITE,
305 					 GSS_C_NO_CHANNEL_BINDINGS,
306 					 &input_token, &http->gssmech,
307 					 &output_token, NULL, NULL);
308 
309     if (input_token.value)
310       free(input_token.value);
311 
312     if (GSS_ERROR(major_status))
313     {
314       cups_gss_printf(major_status, minor_status,
315 		      "cupsDoAuthentication: Unable to initialize security "
316 		      "context");
317       http->status = HTTP_AUTHORIZATION_CANCELED;
318 
319       return (-1);
320     }
321 
322     if (major_status == GSS_S_CONTINUE_NEEDED)
323       cups_gss_printf(major_status, minor_status,
324 		      "cupsDoAuthentication: Continuation needed!");
325 
326     if (output_token.length > 0 && output_token.length <= 65536)
327     {
328      /*
329       * Allocate the authorization string since Windows KDCs can have
330       * arbitrarily large credentials...
331       */
332 
333       int authsize = 10 +				/* "Negotiate " */
334                      output_token.length * 4 / 3 + 1 +	/* Base64 */
335 		     1;					/* nul */
336 
337       httpSetAuthString(http, NULL, NULL);
338 
339       if ((http->authstring = malloc(authsize)) == NULL)
340       {
341         http->authstring = http->_authstring;
342 	authsize         = sizeof(http->_authstring);
343       }
344 
345       strcpy(http->authstring, "Negotiate ");
346       httpEncode64_2(http->authstring + 10, authsize - 10, output_token.value,
347 		     output_token.length);
348 
349       gss_release_buffer(&minor_status, &output_token);
350     }
351     else
352     {
353       DEBUG_printf(("1cupsDoAuthentication: Kerberos credentials too large - "
354                     "%d bytes!", (int)output_token.length));
355       http->status = HTTP_AUTHORIZATION_CANCELED;
356       gss_release_buffer(&minor_status, &output_token);
357 
358       return (-1);
359     }
360 #endif /* HAVE_GSSAPI */
361   }
362   else if (strncmp(http->fields[HTTP_FIELD_WWW_AUTHENTICATE], "Digest", 6))
363   {
364    /*
365     * Basic authentication...
366     */
367 
368     char	encode[256];		/* Base64 buffer */
369 
370 
371     httpEncode64_2(encode, sizeof(encode), http->userpass,
372                    (int)strlen(http->userpass));
373     httpSetAuthString(http, "Basic", encode);
374   }
375   else
376   {
377    /*
378     * Digest authentication...
379     */
380 
381     char	encode[33],		/* MD5 buffer */
382 		digest[1024];		/* Digest auth data */
383 
384 
385     httpGetSubField(http, HTTP_FIELD_WWW_AUTHENTICATE, "realm", realm);
386     httpGetSubField(http, HTTP_FIELD_WWW_AUTHENTICATE, "nonce", nonce);
387 
388     httpMD5(cupsUser(), realm, strchr(http->userpass, ':') + 1, encode);
389     httpMD5Final(nonce, method, resource, encode);
390     snprintf(digest, sizeof(digest),
391 	     "username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", "
392 	     "response=\"%s\"", cupsUser(), realm, nonce, resource, encode);
393     httpSetAuthString(http, "Digest", digest);
394   }
395 
396   DEBUG_printf(("1cupsDoAuthentication: authstring=\"%s\"", http->authstring));
397 
398   return (0);
399 }
400 
401 
402 #ifdef HAVE_GSSAPI
403 /*
404  * 'cups_get_gssname()' - Get CUPS service credentials for authentication.
405  */
406 
407 static gss_name_t			/* O - Server name */
cups_get_gssname(http_t * http,const char * service_name)408 cups_get_gssname(
409     http_t     *http,			/* I - Connection to server */
410     const char *service_name)		/* I - Service name */
411 {
412   gss_buffer_desc token = GSS_C_EMPTY_BUFFER;
413 					/* Service token */
414   OM_uint32	  major_status,		/* Major status code */
415 		  minor_status;		/* Minor status code */
416   gss_name_t	  server_name;		/* Server name */
417   char		  buf[1024],		/* Name buffer */
418 		  fqdn[HTTP_MAX_URI];	/* Server name buffer */
419 
420 
421   DEBUG_printf(("7cups_get_gssname(http=%p, service_name=\"%s\")", http,
422                 service_name));
423 
424 
425  /*
426   * Get the hostname...
427   */
428 
429   httpGetHostname(http, fqdn, sizeof(fqdn));
430 
431   if (!strcmp(fqdn, "localhost"))
432     httpGetHostname(NULL, fqdn, sizeof(fqdn));
433 
434  /*
435   * Get a server name we can use for authentication purposes...
436   */
437 
438   snprintf(buf, sizeof(buf), "%s@%s", service_name, fqdn);
439 
440   DEBUG_printf(("9cups_get_gssname: Looking up %s...", buf));
441 
442   token.value  = buf;
443   token.length = strlen(buf);
444   server_name  = GSS_C_NO_NAME;
445   major_status = gss_import_name(&minor_status, &token,
446 	 			 GSS_C_NT_HOSTBASED_SERVICE,
447 				 &server_name);
448 
449   if (GSS_ERROR(major_status))
450   {
451     cups_gss_printf(major_status, minor_status,
452                     "cups_get_gssname: gss_import_name() failed");
453     return (NULL);
454   }
455 
456   return (server_name);
457 }
458 
459 
460 #  ifdef DEBUG
461 /*
462  * 'cups_gss_printf()' - Show debug error messages from GSSAPI...
463  */
464 
465 static void
cups_gss_printf(OM_uint32 major_status,OM_uint32 minor_status,const char * message)466 cups_gss_printf(OM_uint32  major_status,/* I - Major status code */
467 		OM_uint32  minor_status,/* I - Minor status code */
468 		const char *message)	/* I - Prefix for error message */
469 {
470   OM_uint32	err_major_status,	/* Major status code for display */
471 		err_minor_status;	/* Minor status code for display */
472   OM_uint32	msg_ctx;		/* Message context */
473   gss_buffer_desc major_status_string = GSS_C_EMPTY_BUFFER,
474 					/* Major status message */
475 		minor_status_string = GSS_C_EMPTY_BUFFER;
476 					/* Minor status message */
477 
478 
479   msg_ctx          = 0;
480   err_major_status = gss_display_status(&err_minor_status,
481 	                        	major_status,
482 					GSS_C_GSS_CODE,
483 					GSS_C_NO_OID,
484 					&msg_ctx,
485 					&major_status_string);
486 
487   if (!GSS_ERROR(err_major_status))
488     gss_display_status(&err_minor_status, minor_status, GSS_C_MECH_CODE,
489 		       GSS_C_NULL_OID, &msg_ctx, &minor_status_string);
490 
491   DEBUG_printf(("1%s: %s, %s", message, (char *)major_status_string.value,
492 	        (char *)minor_status_string.value));
493 
494   gss_release_buffer(&err_minor_status, &major_status_string);
495   gss_release_buffer(&err_minor_status, &minor_status_string);
496 }
497 #  endif /* DEBUG */
498 #endif /* HAVE_GSSAPI */
499 
500 
501 /*
502  * 'cups_local_auth()' - Get the local authorization certificate if
503  *                       available/applicable...
504  */
505 
506 static int				/* O - 0 if available */
507 					/*     1 if not available */
508 					/*    -1 error */
cups_local_auth(http_t * http)509 cups_local_auth(http_t *http)		/* I - HTTP connection to server */
510 {
511 #if defined(WIN32) || defined(__EMX__)
512  /*
513   * Currently WIN32 and OS-2 do not support the CUPS server...
514   */
515 
516   return (1);
517 #else
518   int			pid;		/* Current process ID */
519   FILE			*fp;		/* Certificate file */
520   char			trc[16],	/* Try Root Certificate parameter */
521 			filename[1024],	/* Certificate filename */
522 			certificate[33];/* Certificate string */
523   _cups_globals_t *cg = _cupsGlobals();	/* Global data */
524 #  if defined(HAVE_AUTHORIZATION_H)
525   OSStatus		status;		/* Status */
526   AuthorizationItem	auth_right;	/* Authorization right */
527   AuthorizationRights	auth_rights;	/* Authorization rights */
528   AuthorizationFlags	auth_flags;	/* Authorization flags */
529   AuthorizationExternalForm auth_extrn;	/* Authorization ref external */
530   char			auth_key[1024];	/* Buffer */
531   char			buffer[1024];	/* Buffer */
532 #  endif /* HAVE_AUTHORIZATION_H */
533 
534 
535   DEBUG_printf(("7cups_local_auth(http=%p) hostaddr=%s, hostname=\"%s\"",
536                 http, httpAddrString(http->hostaddr, filename, sizeof(filename)), http->hostname));
537 
538  /*
539   * See if we are accessing localhost...
540   */
541 
542   if (!httpAddrLocalhost(http->hostaddr) &&
543       strcasecmp(http->hostname, "localhost") != 0)
544   {
545     DEBUG_puts("8cups_local_auth: Not a local connection!");
546     return (1);
547   }
548 
549 #  if defined(HAVE_AUTHORIZATION_H)
550  /*
551   * Delete any previous authorization reference...
552   */
553 
554   if (http->auth_ref)
555   {
556     AuthorizationFree(http->auth_ref, kAuthorizationFlagDefaults);
557     http->auth_ref = NULL;
558   }
559 
560   if (!getenv("GATEWAY_INTERFACE") &&
561       httpGetSubField2(http, HTTP_FIELD_WWW_AUTHENTICATE, "authkey",
562 		       auth_key, sizeof(auth_key)))
563   {
564     status = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment,
565 				 kAuthorizationFlagDefaults, &http->auth_ref);
566     if (status != errAuthorizationSuccess)
567     {
568       DEBUG_printf(("8cups_local_auth: AuthorizationCreate() returned %d (%s)",
569 		    (int)status, cssmErrorString(status)));
570       return (-1);
571     }
572 
573     auth_right.name        = auth_key;
574     auth_right.valueLength = 0;
575     auth_right.value       = NULL;
576     auth_right.flags       = 0;
577 
578     auth_rights.count = 1;
579     auth_rights.items = &auth_right;
580 
581     auth_flags = kAuthorizationFlagDefaults |
582 		 kAuthorizationFlagPreAuthorize |
583 		 kAuthorizationFlagInteractionAllowed |
584 		 kAuthorizationFlagExtendRights;
585 
586     status = AuthorizationCopyRights(http->auth_ref, &auth_rights,
587 				     kAuthorizationEmptyEnvironment,
588 				     auth_flags, NULL);
589     if (status == errAuthorizationSuccess)
590       status = AuthorizationMakeExternalForm(http->auth_ref, &auth_extrn);
591 
592     if (status == errAuthorizationSuccess)
593     {
594      /*
595       * Set the authorization string and return...
596       */
597 
598       httpEncode64_2(buffer, sizeof(buffer), (void *)&auth_extrn,
599 		     sizeof(auth_extrn));
600 
601       httpSetAuthString(http, "AuthRef", buffer);
602 
603       DEBUG_printf(("8cups_local_auth: Returning authstring=\"%s\"",
604 		    http->authstring));
605       return (0);
606     }
607     else if (status == errAuthorizationCanceled)
608       return (-1);
609 
610     DEBUG_printf(("9cups_local_auth: AuthorizationCopyRights() returned %d (%s)",
611 		  (int)status, cssmErrorString(status)));
612 
613   /*
614    * Fall through to try certificates...
615    */
616   }
617 #  endif /* HAVE_AUTHORIZATION_H */
618 
619 #  if defined(SO_PEERCRED) && defined(AF_LOCAL)
620  /*
621   * See if we can authenticate using the peer credentials provided over a
622   * domain socket; if so, specify "PeerCred username" as the authentication
623   * information...
624   */
625 
626 #    ifdef HAVE_GSSAPI
627   if (strncmp(http->fields[HTTP_FIELD_WWW_AUTHENTICATE], "Negotiate", 9) &&
628 #    else
629   if (
630 #    endif /* HAVE_GSSAPI */
631       http->hostaddr->addr.sa_family == AF_LOCAL &&
632       !getenv("GATEWAY_INTERFACE"))	/* Not via CGI programs... */
633   {
634    /*
635     * Verify that the current cupsUser() matches the current UID...
636     */
637 
638     struct passwd	*pwd;		/* Password information */
639     const char		*username;	/* Current username */
640 
641     username = cupsUser();
642 
643     if ((pwd = getpwnam(username)) != NULL && pwd->pw_uid == getuid())
644     {
645       httpSetAuthString(http, "PeerCred", username);
646 
647       DEBUG_printf(("8cups_local_auth: Returning authstring=\"%s\"",
648 		    http->authstring));
649 
650       return (0);
651     }
652   }
653 #  endif /* SO_PEERCRED && AF_LOCAL */
654 
655  /*
656   * Try opening a certificate file for this PID.  If that fails,
657   * try the root certificate...
658   */
659 
660   pid = getpid();
661   snprintf(filename, sizeof(filename), "%s/certs/%d", cg->cups_statedir, pid);
662   if ((fp = fopen(filename, "r")) == NULL && pid > 0)
663   {
664    /*
665     * No certificate for this PID; see if we can get the root certificate...
666     */
667 
668     DEBUG_printf(("9cups_local_auth: Unable to open file %s: %s",
669                   filename, strerror(errno)));
670 
671 #ifdef HAVE_GSSAPI
672     if (!strncmp(http->fields[HTTP_FIELD_WWW_AUTHENTICATE], "Negotiate", 9))
673     {
674      /*
675       * Kerberos required, don't try the root certificate...
676       */
677 
678       return (1);
679     }
680 #endif /* HAVE_GSSAPI */
681 
682     if (!httpGetSubField2(http, HTTP_FIELD_WWW_AUTHENTICATE, "trc", trc,
683 	                  sizeof(trc)))
684     {
685      /*
686       * Scheduler doesn't want us to use the root certificate...
687       */
688 
689       return (1);
690     }
691 
692     snprintf(filename, sizeof(filename), "%s/certs/0", cg->cups_statedir);
693     fp = fopen(filename, "r");
694   }
695 
696   if (fp)
697   {
698    /*
699     * Read the certificate from the file...
700     */
701 
702     fgets(certificate, sizeof(certificate), fp);
703     fclose(fp);
704 
705    /*
706     * Set the authorization string and return...
707     */
708 
709     httpSetAuthString(http, "Local", certificate);
710 
711     DEBUG_printf(("8cups_local_auth: Returning authstring=\"%s\"",
712 		  http->authstring));
713 
714     return (0);
715   }
716 
717   return (1);
718 #endif /* WIN32 || __EMX__ */
719 }
720 
721 
722 /*
723  * End of "$Id: auth.c 9233 2010-08-10 06:15:55Z mike $".
724  */
725