]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/auth.c
Kerberos fixes for Mac OS X (STR #2045).
[thirdparty/cups.git] / cups / auth.c
1 /*
2 * "$Id$"
3 *
4 * Authentication functions for the Common UNIX Printing System (CUPS).
5 *
6 * Copyright 1997-2006 by Easy Software Products.
7 *
8 * This file contains Kerberos support code, copyright 2006 by
9 * Jelmer Vernooij.
10 *
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
16 * at:
17 *
18 * Attn: CUPS Licensing Information
19 * Easy Software Products
20 * 44141 Airport View Drive, Suite 204
21 * Hollywood, Maryland 20636 USA
22 *
23 * Voice: (301) 373-9600
24 * EMail: cups-info@cups.org
25 * WWW: http://www.cups.org
26 *
27 * This file is subject to the Apple OS-Developed Software exception.
28 *
29 * Contents:
30 *
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...
36 */
37
38 /*
39 * Include necessary headers...
40 */
41
42 #include "globals.h"
43 #include "debug.h"
44 #include <stdlib.h>
45 #include <ctype.h>
46 #include <errno.h>
47 #include <fcntl.h>
48 #include <sys/stat.h>
49 #if defined(WIN32) || defined(__EMX__)
50 # include <io.h>
51 #else
52 # include <unistd.h>
53 #endif /* WIN32 || __EMX__ */
54
55
56 /*
57 * Local functions...
58 */
59
60 #ifdef HAVE_GSSAPI
61 # ifdef DEBUG
62 static void DEBUG_gss_printf(OM_uint32 major_status, OM_uint32 minor_status,
63 const char *message);
64 # endif /* DEBUG */
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);
68
69
70 /*
71 * 'cupsDoAuthentication()' - Authenticate a request.
72 *
73 * This function should be called in response to a HTTP_UNAUTHORIZED
74 * status, prior to resubmitting your request.
75 *
76 * @since CUPS 1.1.20@
77 */
78
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) */
82 const char *resource)
83 /* I - Resource path */
84 {
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 */
90
91
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)));
98
99 /*
100 * Clear the current authentication string...
101 */
102
103 http->_authstring[0] = '\0';
104
105 if (http->authstring && http->authstring != http->_authstring)
106 free(http->authstring);
107
108 http->authstring = http->_authstring;
109
110 /*
111 * See if we can do local authentication...
112 */
113
114 if (http->digest_tries < 3 && !cups_local_auth(http))
115 {
116 DEBUG_printf(("cupsDoAuthentication: authstring=\"%s\"\n", http->authstring));
117
118 if (http->status == HTTP_UNAUTHORIZED)
119 http->digest_tries ++;
120
121 return (0);
122 }
123
124 /*
125 * Nope, see if we should retry the current username:password...
126 */
127
128 if ((http->digest_tries > 1 || !http->userpass[0]) &&
129 strncmp(http->fields[HTTP_FIELD_WWW_AUTHENTICATE], "Negotiate", 9))
130 {
131 /*
132 * Nope - get a new password from the user...
133 */
134
135 snprintf(prompt, sizeof(prompt), _("Password for %s on %s? "), cupsUser(),
136 http->hostname[0] == '/' ? "localhost" : http->hostname);
137
138 http->digest_tries = strncasecmp(http->fields[HTTP_FIELD_WWW_AUTHENTICATE],
139 "Digest", 5) != 0;
140 http->userpass[0] = '\0';
141
142 if ((password = cupsGetPassword(prompt)) == NULL)
143 return (-1);
144
145 if (!password[0])
146 return (-1);
147
148 snprintf(http->userpass, sizeof(http->userpass), "%s:%s", cupsUser(),
149 password);
150 }
151 else if (http->status == HTTP_UNAUTHORIZED)
152 http->digest_tries ++;
153
154 /*
155 * Got a password; encode it for the server...
156 */
157
158 if (!strncmp(http->fields[HTTP_FIELD_WWW_AUTHENTICATE], "Negotiate", 9))
159 {
160 #ifdef HAVE_GSSAPI
161 /*
162 * Kerberos authentication...
163 */
164
165 OM_uint32 minor_status, /* Minor status code */
166 major_status; /* Major status code */
167 gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER,
168 /* Output token */
169 input_token = GSS_C_EMPTY_BUFFER;
170 /* Input token */
171 char *gss_service_name;
172 /* GSS service name */
173 const char *authorization;
174 /* Pointer into Authorization string */
175
176
177 if (http->gssname == GSS_C_NO_NAME)
178 {
179 if ((gss_service_name = getenv("CUPS_GSSSERVICENAME")) == NULL)
180 gss_service_name = CUPS_DEFAULT_GSSSERVICENAME;
181 else
182 DEBUG_puts("cupsDoAuthentication: GSS service name set via environment");
183
184 http->gssname = cups_get_gss_creds(http, gss_service_name);
185 }
186
187 /*
188 * Find the start of the Kerberos input token...
189 */
190
191 authorization = httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE);
192
193 authorization += 9;
194 while (*authorization && isspace(*authorization & 255))
195 authorization ++;
196
197 if (*authorization)
198 {
199 /*
200 * For SPNEGO, this is where we'll feed the server's authorization data
201 * back into gss via input_token...
202 */
203 }
204 else
205 {
206 if (http->gssctx != GSS_C_NO_CONTEXT)
207 {
208 major_status = gss_delete_sec_context(&minor_status, &http->gssctx,
209 GSS_C_NO_BUFFER);
210 http->gssctx = GSS_C_NO_CONTEXT;
211 }
212 }
213
214 major_status = gss_init_sec_context(&minor_status, GSS_C_NO_CREDENTIAL,
215 &http->gssctx,
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);
221
222 if (input_token.value)
223 free(input_token.value);
224
225 if (GSS_ERROR(major_status))
226 {
227 # ifdef DEBUG
228 DEBUG_gss_printf(major_status, minor_status,
229 "Unable to initialise security context");
230 # endif /* DEBUG */
231 return (-1);
232 }
233
234 # ifdef DEBUG
235 if (major_status == GSS_S_CONTINUE_NEEDED)
236 DEBUG_gss_printf(major_status, minor_status, "Continuation needed!");
237 # endif /* DEBUG */
238
239 if (output_token.length)
240 {
241 httpEncode64_2(encode, sizeof(encode), output_token.value,
242 output_token.length);
243
244 http->authstring = malloc(strlen(encode) + 11);
245 sprintf(http->authstring, "Negotiate %s", encode); /* Safe because allocated */
246
247 major_status = gss_release_buffer(&minor_status, &output_token);
248 }
249
250 /*
251 * Copy back what we can to _authstring for backwards compatibility...
252 */
253
254 strlcpy(http->_authstring, http->authstring, sizeof(http->_authstring));
255 #endif /* HAVE_GSSAPI */
256 }
257 else if (strncmp(http->fields[HTTP_FIELD_WWW_AUTHENTICATE], "Digest", 6))
258 {
259 /*
260 * Basic authentication...
261 */
262
263 httpEncode64_2(encode, sizeof(encode), http->userpass,
264 strlen(http->userpass));
265 snprintf(http->_authstring, sizeof(http->_authstring), "Basic %s", encode);
266 }
267 else
268 {
269 /*
270 * Digest authentication...
271 */
272
273 httpGetSubField(http, HTTP_FIELD_WWW_AUTHENTICATE, "realm", realm);
274 httpGetSubField(http, HTTP_FIELD_WWW_AUTHENTICATE, "nonce", nonce);
275
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,
281 resource, encode);
282 }
283
284 DEBUG_printf(("cupsDoAuthentication: authstring=\"%s\"\n", http->authstring));
285
286 return (0);
287 }
288
289
290 #ifdef HAVE_GSSAPI
291 # ifdef DEBUG
292 /*
293 * 'DEBUG_gss_printf()' - Show debug error messages from GSSAPI...
294 */
295
296 static void
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 */
300 {
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 */
308
309
310 msg_ctx = 0;
311 err_major_status = gss_display_status(&err_minor_status,
312 major_status,
313 GSS_C_GSS_CODE,
314 GSS_C_NO_OID,
315 &msg_ctx,
316 &major_status_string);
317
318 if (!GSS_ERROR(err_major_status))
319 err_major_status = gss_display_status(&err_minor_status,
320 minor_status,
321 GSS_C_MECH_CODE,
322 GSS_C_NULL_OID,
323 &msg_ctx,
324 &minor_status_string);
325
326 printf("%s: %s, %s\n", message, (char *)major_status_string.value,
327 (char *)minor_status_string.value);
328
329 gss_release_buffer(&err_minor_status, &major_status_string);
330 gss_release_buffer(&err_minor_status, &minor_status_string);
331 }
332 # endif /* DEBUG */
333
334
335 /*
336 * 'cups_get_gss_creds()' - Get CUPS service credentials for authentication.
337 */
338
339 static gss_name_t /* O - Server name */
340 cups_get_gss_creds(
341 http_t *http, /* I - Connection to server */
342 const char *service_name) /* I - Service name */
343 {
344 gss_buffer_desc token = GSS_C_EMPTY_BUFFER;
345 /* Service token */
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 */
351
352
353 /*
354 * Get a server name we can use for authentication purposes...
355 */
356
357 snprintf(buf, sizeof(buf), "%s@%s", service_name,
358 httpGetHostname(http, fqdn, sizeof(fqdn)));
359
360 token.value = buf;
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,
365 &server_name);
366
367 if (GSS_ERROR(major_status))
368 {
369 # ifdef DEBUG
370 DEBUG_gss_printf(major_status, minor_status, "gss_import_name() failed");
371 # endif /* DEBUG */
372
373 return (NULL);
374 }
375
376 return (server_name);
377 }
378 #endif /* HAVE_GSSAPI */
379
380
381 /*
382 * 'cups_local_auth()' - Get the local authorization certificate if
383 * available/applicable...
384 */
385
386 static int /* O - 0 if available, -1 if not */
387 cups_local_auth(http_t *http) /* I - HTTP connection to server */
388 {
389 #if defined(WIN32) || defined(__EMX__)
390 /*
391 * Currently WIN32 and OS-2 do not support the CUPS server...
392 */
393
394 return (-1);
395 #else
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 */
401
402
403 DEBUG_printf(("cups_local_auth(http=%p) hostaddr=%s, hostname=\"%s\"\n",
404 http, httpAddrString(http->hostaddr, filename, sizeof(filename)), http->hostname));
405
406 /*
407 * See if we are accessing localhost...
408 */
409
410 if (!httpAddrLocalhost(http->hostaddr) &&
411 strcasecmp(http->hostname, "localhost") != 0)
412 {
413 DEBUG_puts("cups_local_auth: Not a local connection!");
414 return (-1);
415 }
416
417 /*
418 * Try opening a certificate file for this PID. If that fails,
419 * try the root certificate...
420 */
421
422 pid = getpid();
423 snprintf(filename, sizeof(filename), "%s/certs/%d", cg->cups_statedir, pid);
424 if ((fp = fopen(filename, "r")) == NULL && pid > 0)
425 {
426 DEBUG_printf(("cups_local_auth: Unable to open file %s: %s\n",
427 filename, strerror(errno)));
428
429 snprintf(filename, sizeof(filename), "%s/certs/0", cg->cups_statedir);
430 fp = fopen(filename, "r");
431 }
432
433 if (fp == NULL)
434 {
435 DEBUG_printf(("cups_local_auth: Unable to open file %s: %s\n",
436 filename, strerror(errno)));
437 return (-1);
438 }
439
440 /*
441 * Read the certificate from the file...
442 */
443
444 fgets(certificate, sizeof(certificate), fp);
445 fclose(fp);
446
447 /*
448 * Set the authorization string and return...
449 */
450
451 http->authstring = malloc(strlen(certificate) + 10);
452 sprintf(http->authstring, "Local %s", certificate);
453
454 /* Copy back to _authstring for backwards compatibility */
455 strlcpy(http->_authstring, http->authstring, sizeof(http->_authstring));
456
457 DEBUG_printf(("cups_local_auth: Returning authstring = \"%s\"\n",
458 http->authstring));
459
460 return (0);
461 #endif /* WIN32 || __EMX__ */
462 }
463
464
465 /*
466 * End of "$Id$".
467 */