4 * Authentication functions for the Common UNIX Printing System (CUPS).
6 * Copyright 1997-2006 by Easy Software Products.
8 * This file contains Kerberos support code, copyright 2006 by
11 * These coded instructions, statements, and computer programs are the
12 * property of Easy Software Products and are protected by Federal
13 * copyright law. Distribution and use rights are outlined in the file
14 * "LICENSE.txt" which should have been included with this file. If this
15 * file is missing or damaged please contact Easy Software Products
18 * Attn: CUPS Licensing Information
19 * Easy Software Products
20 * 44141 Airport View Drive, Suite 204
21 * Hollywood, Maryland 20636 USA
23 * Voice: (301) 373-9600
24 * EMail: cups-info@cups.org
25 * WWW: http://www.cups.org
27 * This file is subject to the Apple OS-Developed Software exception.
31 * cupsDoAuthentication() - Authenticate a request.
32 * DEBUG_gss_printf() - Show debug error messages from GSSAPI...
33 * cups_get_gss_creds() - Get CUPS service credentials for authentication.
34 * cups_local_auth() - Get the local authorization certificate if
35 * available/applicable...
39 * Include necessary headers...
49 #if defined(WIN32) || defined(__EMX__)
53 #endif /* WIN32 || __EMX__ */
62 static void DEBUG_gss_printf(OM_uint32 major_status
, OM_uint32 minor_status
,
65 static gss_name_t
cups_get_gss_creds(http_t
*http
, const char *service_name
);
66 #endif /* HAVE_GSSAPI */
67 static int cups_local_auth(http_t
*http
);
71 * 'cupsDoAuthentication()' - Authenticate a request.
73 * This function should be called in response to a HTTP_UNAUTHORIZED
74 * status, prior to resubmitting your request.
79 int /* O - 0 on success, -1 on error */
80 cupsDoAuthentication(http_t
*http
, /* I - HTTP connection to server */
81 const char *method
,/* I - Request method (GET, POST, PUT) */
83 /* I - Resource path */
85 const char *password
; /* Password string */
86 char prompt
[1024], /* Prompt for user */
87 realm
[HTTP_MAX_VALUE
], /* realm="xyz" string */
88 nonce
[HTTP_MAX_VALUE
], /* nonce="xyz" string */
89 encode
[2048]; /* Encoded username:password */
92 DEBUG_printf(("cupsDoAuthentication(http=%p, method=\"%s\", resource=\"%s\")\n",
93 http
, method
, resource
));
94 DEBUG_printf(("cupsDoAuthentication: digest_tries=%d, userpass=\"%s\"\n",
95 http
->digest_tries
, http
->userpass
));
96 DEBUG_printf(("cupsDoAuthentication: WWW-Authenticate=\"%s\"\n",
97 httpGetField(http
, HTTP_FIELD_WWW_AUTHENTICATE
)));
100 * Clear the current authentication string...
103 http
->_authstring
[0] = '\0';
105 if (http
->authstring
&& http
->authstring
!= http
->_authstring
)
106 free(http
->authstring
);
108 http
->authstring
= http
->_authstring
;
111 * See if we can do local authentication...
114 if (http
->digest_tries
< 3 && !cups_local_auth(http
))
116 DEBUG_printf(("cupsDoAuthentication: authstring=\"%s\"\n", http
->authstring
));
118 if (http
->status
== HTTP_UNAUTHORIZED
)
119 http
->digest_tries
++;
125 * Nope, see if we should retry the current username:password...
128 if ((http
->digest_tries
> 1 || !http
->userpass
[0]) &&
129 strncmp(http
->fields
[HTTP_FIELD_WWW_AUTHENTICATE
], "Negotiate", 9))
132 * Nope - get a new password from the user...
135 snprintf(prompt
, sizeof(prompt
), _("Password for %s on %s? "), cupsUser(),
136 http
->hostname
[0] == '/' ? "localhost" : http
->hostname
);
138 http
->digest_tries
= strncasecmp(http
->fields
[HTTP_FIELD_WWW_AUTHENTICATE
],
140 http
->userpass
[0] = '\0';
142 if ((password
= cupsGetPassword(prompt
)) == NULL
)
148 snprintf(http
->userpass
, sizeof(http
->userpass
), "%s:%s", cupsUser(),
151 else if (http
->status
== HTTP_UNAUTHORIZED
)
152 http
->digest_tries
++;
155 * Got a password; encode it for the server...
158 if (!strncmp(http
->fields
[HTTP_FIELD_WWW_AUTHENTICATE
], "Negotiate", 9))
162 * Kerberos authentication...
165 OM_uint32 minor_status
, /* Minor status code */
166 major_status
; /* Major status code */
167 gss_buffer_desc output_token
= GSS_C_EMPTY_BUFFER
,
169 input_token
= GSS_C_EMPTY_BUFFER
;
171 char *gss_service_name
;
172 /* GSS service name */
173 const char *authorization
;
174 /* Pointer into Authorization string */
177 if (http
->gssname
== GSS_C_NO_NAME
)
179 if ((gss_service_name
= getenv("CUPS_GSSSERVICENAME")) == NULL
)
180 gss_service_name
= CUPS_DEFAULT_GSSSERVICENAME
;
182 DEBUG_puts("cupsDoAuthentication: GSS service name set via environment");
184 http
->gssname
= cups_get_gss_creds(http
, gss_service_name
);
188 * Find the start of the Kerberos input token...
191 authorization
= httpGetField(http
, HTTP_FIELD_WWW_AUTHENTICATE
);
194 while (*authorization
&& isspace(*authorization
& 255))
200 * For SPNEGO, this is where we'll feed the server's authorization data
201 * back into gss via input_token...
206 if (http
->gssctx
!= GSS_C_NO_CONTEXT
)
208 major_status
= gss_delete_sec_context(&minor_status
, &http
->gssctx
,
210 http
->gssctx
= GSS_C_NO_CONTEXT
;
214 major_status
= gss_init_sec_context(&minor_status
, GSS_C_NO_CREDENTIAL
,
216 http
->gssname
, http
->gssmech
,
217 GSS_C_MUTUAL_FLAG
, GSS_C_INDEFINITE
,
218 GSS_C_NO_CHANNEL_BINDINGS
,
219 &input_token
, &http
->gssmech
,
220 &output_token
, NULL
, NULL
);
222 if (input_token
.value
)
223 free(input_token
.value
);
225 if (GSS_ERROR(major_status
))
228 DEBUG_gss_printf(major_status
, minor_status
,
229 "Unable to initialise security context");
235 if (major_status
== GSS_S_CONTINUE_NEEDED
)
236 DEBUG_gss_printf(major_status
, minor_status
, "Continuation needed!");
239 if (output_token
.length
)
241 httpEncode64_2(encode
, sizeof(encode
), output_token
.value
,
242 output_token
.length
);
244 http
->authstring
= malloc(strlen(encode
) + 11);
245 sprintf(http
->authstring
, "Negotiate %s", encode
); /* Safe because allocated */
247 major_status
= gss_release_buffer(&minor_status
, &output_token
);
251 * Copy back what we can to _authstring for backwards compatibility...
254 strlcpy(http
->_authstring
, http
->authstring
, sizeof(http
->_authstring
));
255 #endif /* HAVE_GSSAPI */
257 else if (strncmp(http
->fields
[HTTP_FIELD_WWW_AUTHENTICATE
], "Digest", 6))
260 * Basic authentication...
263 httpEncode64_2(encode
, sizeof(encode
), http
->userpass
,
264 strlen(http
->userpass
));
265 snprintf(http
->_authstring
, sizeof(http
->_authstring
), "Basic %s", encode
);
270 * Digest authentication...
273 httpGetSubField(http
, HTTP_FIELD_WWW_AUTHENTICATE
, "realm", realm
);
274 httpGetSubField(http
, HTTP_FIELD_WWW_AUTHENTICATE
, "nonce", nonce
);
276 httpMD5(cupsUser(), realm
, strchr(http
->userpass
, ':') + 1, encode
);
277 httpMD5Final(nonce
, method
, resource
, encode
);
278 snprintf(http
->_authstring
, sizeof(http
->_authstring
),
279 "Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", "
280 "uri=\"%s\", response=\"%s\"", cupsUser(), realm
, nonce
,
284 DEBUG_printf(("cupsDoAuthentication: authstring=\"%s\"\n", http
->authstring
));
293 * 'DEBUG_gss_printf()' - Show debug error messages from GSSAPI...
297 DEBUG_gss_printf(OM_uint32 major_status
,/* I - Major status code */
298 OM_uint32 minor_status
,/* I - Minor status code */
299 const char *message
) /* I - Prefix for error message */
301 OM_uint32 err_major_status
, /* Major status code for display */
302 err_minor_status
; /* Minor status code for display */
303 OM_uint32 msg_ctx
; /* Message context */
304 gss_buffer_desc major_status_string
= GSS_C_EMPTY_BUFFER
,
305 /* Major status message */
306 minor_status_string
= GSS_C_EMPTY_BUFFER
;
307 /* Minor status message */
311 err_major_status
= gss_display_status(&err_minor_status
,
316 &major_status_string
);
318 if (!GSS_ERROR(err_major_status
))
319 err_major_status
= gss_display_status(&err_minor_status
,
324 &minor_status_string
);
326 printf("%s: %s, %s\n", message
, (char *)major_status_string
.value
,
327 (char *)minor_status_string
.value
);
329 gss_release_buffer(&err_minor_status
, &major_status_string
);
330 gss_release_buffer(&err_minor_status
, &minor_status_string
);
336 * 'cups_get_gss_creds()' - Get CUPS service credentials for authentication.
339 static gss_name_t
/* O - Server name */
341 http_t
*http
, /* I - Connection to server */
342 const char *service_name
) /* I - Service name */
344 gss_buffer_desc token
= GSS_C_EMPTY_BUFFER
;
346 OM_uint32 major_status
, /* Major status code */
347 minor_status
; /* Minor status code */
348 gss_name_t server_name
; /* Server name */
349 char buf
[1024], /* Name buffer */
350 fqdn
[HTTP_MAX_URI
]; /* Server name buffer */
354 * Get a server name we can use for authentication purposes...
357 snprintf(buf
, sizeof(buf
), "%s@%s", service_name
,
358 httpGetHostname(http
, fqdn
, sizeof(fqdn
)));
361 token
.length
= strlen(buf
);
362 server_name
= GSS_C_NO_NAME
;
363 major_status
= gss_import_name(&minor_status
, &token
,
364 GSS_C_NT_HOSTBASED_SERVICE
,
367 if (GSS_ERROR(major_status
))
370 DEBUG_gss_printf(major_status
, minor_status
, "gss_import_name() failed");
376 return (server_name
);
378 #endif /* HAVE_GSSAPI */
382 * 'cups_local_auth()' - Get the local authorization certificate if
383 * available/applicable...
386 static int /* O - 0 if available, -1 if not */
387 cups_local_auth(http_t
*http
) /* I - HTTP connection to server */
389 #if defined(WIN32) || defined(__EMX__)
391 * Currently WIN32 and OS-2 do not support the CUPS server...
396 int pid
; /* Current process ID */
397 FILE *fp
; /* Certificate file */
398 char filename
[1024], /* Certificate filename */
399 certificate
[33]; /* Certificate string */
400 _cups_globals_t
*cg
= _cupsGlobals(); /* Global data */
403 DEBUG_printf(("cups_local_auth(http=%p) hostaddr=%s, hostname=\"%s\"\n",
404 http
, httpAddrString(http
->hostaddr
, filename
, sizeof(filename
)), http
->hostname
));
407 * See if we are accessing localhost...
410 if (!httpAddrLocalhost(http
->hostaddr
) &&
411 strcasecmp(http
->hostname
, "localhost") != 0)
413 DEBUG_puts("cups_local_auth: Not a local connection!");
418 * Try opening a certificate file for this PID. If that fails,
419 * try the root certificate...
423 snprintf(filename
, sizeof(filename
), "%s/certs/%d", cg
->cups_statedir
, pid
);
424 if ((fp
= fopen(filename
, "r")) == NULL
&& pid
> 0)
426 DEBUG_printf(("cups_local_auth: Unable to open file %s: %s\n",
427 filename
, strerror(errno
)));
429 snprintf(filename
, sizeof(filename
), "%s/certs/0", cg
->cups_statedir
);
430 fp
= fopen(filename
, "r");
435 DEBUG_printf(("cups_local_auth: Unable to open file %s: %s\n",
436 filename
, strerror(errno
)));
441 * Read the certificate from the file...
444 fgets(certificate
, sizeof(certificate
), fp
);
448 * Set the authorization string and return...
451 http
->authstring
= malloc(strlen(certificate
) + 10);
452 sprintf(http
->authstring
, "Local %s", certificate
);
454 /* Copy back to _authstring for backwards compatibility */
455 strlcpy(http
->_authstring
, http
->authstring
, sizeof(http
->_authstring
));
457 DEBUG_printf(("cups_local_auth: Returning authstring = \"%s\"\n",
461 #endif /* WIN32 || __EMX__ */