]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/auth.c
Load cups into easysw/current.
[thirdparty/cups.git] / cups / auth.c
1 /*
2 * "$Id: auth.c 6397 2007-03-25 23:33:32Z mike $"
3 *
4 * Authentication functions for the Common UNIX Printing System (CUPS).
5 *
6 * Copyright 1997-2007 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 #if HAVE_AUTHORIZATION_H
56 # include <Security/Authorization.h>
57 # ifdef HAVE_SECBASEPRIV_H
58 # include <Security/SecBasePriv.h>
59 # else
60 extern const char *cssmErrorString(int error);
61 # endif /* HAVE_SECBASEPRIV_H */
62 #endif /* HAVE_AUTHORIZATION_H */
63
64
65 /*
66 * Local functions...
67 */
68
69 #ifdef HAVE_GSSAPI
70 # ifdef DEBUG
71 static void DEBUG_gss_printf(OM_uint32 major_status, OM_uint32 minor_status,
72 const char *message);
73 # endif /* DEBUG */
74 static gss_name_t cups_get_gss_creds(http_t *http, const char *service_name);
75 #endif /* HAVE_GSSAPI */
76 static int cups_local_auth(http_t *http);
77
78
79 /*
80 * 'cupsDoAuthentication()' - Authenticate a request.
81 *
82 * This function should be called in response to a HTTP_UNAUTHORIZED
83 * status, prior to resubmitting your request.
84 *
85 * @since CUPS 1.1.20@
86 */
87
88 int /* O - 0 on success, -1 on error */
89 cupsDoAuthentication(http_t *http, /* I - HTTP connection to server */
90 const char *method,/* I - Request method (GET, POST, PUT) */
91 const char *resource)
92 /* I - Resource path */
93 {
94 const char *password; /* Password string */
95 char prompt[1024], /* Prompt for user */
96 realm[HTTP_MAX_VALUE], /* realm="xyz" string */
97 nonce[HTTP_MAX_VALUE], /* nonce="xyz" string */
98 encode[2048]; /* Encoded username:password */
99 int localauth; /* Local authentication result */
100 _cups_globals_t *cg; /* Global data */
101
102
103 DEBUG_printf(("cupsDoAuthentication(http=%p, method=\"%s\", resource=\"%s\")\n",
104 http, method, resource));
105 DEBUG_printf(("cupsDoAuthentication: digest_tries=%d, userpass=\"%s\"\n",
106 http->digest_tries, http->userpass));
107 DEBUG_printf(("cupsDoAuthentication: WWW-Authenticate=\"%s\"\n",
108 httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE)));
109
110 /*
111 * Clear the current authentication string...
112 */
113
114 http->_authstring[0] = '\0';
115
116 if (http->authstring && http->authstring != http->_authstring)
117 free(http->authstring);
118
119 http->authstring = http->_authstring;
120
121 /*
122 * See if we can do local authentication...
123 */
124
125 if (http->digest_tries < 3)
126 {
127 if ((localauth = cups_local_auth(http)) == 0)
128 {
129 DEBUG_printf(("cupsDoAuthentication: authstring=\"%s\"\n",
130 http->authstring));
131
132 if (http->status == HTTP_UNAUTHORIZED)
133 http->digest_tries ++;
134
135 return (0);
136 }
137 else if (localauth == -1)
138 return (-1); /* Error or canceled */
139 }
140
141 /*
142 * Nope, see if we should retry the current username:password...
143 */
144
145 if ((http->digest_tries > 1 || !http->userpass[0]) &&
146 strncmp(http->fields[HTTP_FIELD_WWW_AUTHENTICATE], "Negotiate", 9))
147 {
148 /*
149 * Nope - get a new password from the user...
150 */
151
152 cg = _cupsGlobals();
153
154 if (!cg->lang_default)
155 cg->lang_default = cupsLangDefault();
156
157 snprintf(prompt, sizeof(prompt),
158 _cupsLangString(cg->lang_default, _("Password for %s on %s? ")),
159 cupsUser(),
160 http->hostname[0] == '/' ? "localhost" : http->hostname);
161
162 http->digest_tries = strncasecmp(http->fields[HTTP_FIELD_WWW_AUTHENTICATE],
163 "Digest", 5) != 0;
164 http->userpass[0] = '\0';
165
166 if ((password = cupsGetPassword(prompt)) == NULL)
167 return (-1);
168
169 if (!password[0])
170 return (-1);
171
172 snprintf(http->userpass, sizeof(http->userpass), "%s:%s", cupsUser(),
173 password);
174 }
175 else if (http->status == HTTP_UNAUTHORIZED)
176 http->digest_tries ++;
177
178 /*
179 * Got a password; encode it for the server...
180 */
181
182 if (!strncmp(http->fields[HTTP_FIELD_WWW_AUTHENTICATE], "Negotiate", 9))
183 {
184 #ifdef HAVE_GSSAPI
185 /*
186 * Kerberos authentication...
187 */
188
189 OM_uint32 minor_status, /* Minor status code */
190 major_status; /* Major status code */
191 gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER,
192 /* Output token */
193 input_token = GSS_C_EMPTY_BUFFER;
194 /* Input token */
195 char *gss_service_name;
196 /* GSS service name */
197 const char *authorization;
198 /* Pointer into Authorization string */
199
200
201 # ifdef __APPLE__
202 /*
203 * If the weak-linked GSSAPI/Kerberos library is not present, don't try
204 * to use it...
205 */
206
207 if (gss_init_sec_context == NULL)
208 {
209 DEBUG_puts("cupsDoAuthentication: Weak-linked GSSAPI/Kerberos framework "
210 "is not present");
211 return (-1);
212 }
213 # endif /* __APPLE__ */
214
215 if (http->gssname == GSS_C_NO_NAME)
216 {
217 if ((gss_service_name = getenv("CUPS_GSSSERVICENAME")) == NULL)
218 gss_service_name = CUPS_DEFAULT_GSSSERVICENAME;
219 else
220 DEBUG_puts("cupsDoAuthentication: GSS service name set via environment");
221
222 http->gssname = cups_get_gss_creds(http, gss_service_name);
223 }
224
225 /*
226 * Find the start of the Kerberos input token...
227 */
228
229 authorization = httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE);
230
231 authorization += 9;
232 while (*authorization && isspace(*authorization & 255))
233 authorization ++;
234
235 if (*authorization)
236 {
237 /*
238 * For SPNEGO, this is where we'll feed the server's authorization data
239 * back into gss via input_token...
240 */
241 }
242 else
243 {
244 if (http->gssctx != GSS_C_NO_CONTEXT)
245 {
246 major_status = gss_delete_sec_context(&minor_status, &http->gssctx,
247 GSS_C_NO_BUFFER);
248 http->gssctx = GSS_C_NO_CONTEXT;
249 }
250 }
251
252 major_status = gss_init_sec_context(&minor_status, GSS_C_NO_CREDENTIAL,
253 &http->gssctx,
254 http->gssname, http->gssmech,
255 GSS_C_MUTUAL_FLAG, GSS_C_INDEFINITE,
256 GSS_C_NO_CHANNEL_BINDINGS,
257 &input_token, &http->gssmech,
258 &output_token, NULL, NULL);
259
260 if (input_token.value)
261 free(input_token.value);
262
263 if (GSS_ERROR(major_status))
264 {
265 # ifdef DEBUG
266 DEBUG_gss_printf(major_status, minor_status,
267 "Unable to initialise security context");
268 # endif /* DEBUG */
269 return (-1);
270 }
271
272 # ifdef DEBUG
273 if (major_status == GSS_S_CONTINUE_NEEDED)
274 DEBUG_gss_printf(major_status, minor_status, "Continuation needed!");
275 # endif /* DEBUG */
276
277 if (output_token.length)
278 {
279 httpEncode64_2(encode, sizeof(encode), output_token.value,
280 output_token.length);
281
282 http->authstring = malloc(strlen(encode) + 11);
283 sprintf(http->authstring, "Negotiate %s", encode); /* Safe because allocated */
284
285 major_status = gss_release_buffer(&minor_status, &output_token);
286 }
287
288 /*
289 * Copy back what we can to _authstring for backwards compatibility...
290 */
291
292 strlcpy(http->_authstring, http->authstring, sizeof(http->_authstring));
293 #endif /* HAVE_GSSAPI */
294 }
295 else if (strncmp(http->fields[HTTP_FIELD_WWW_AUTHENTICATE], "Digest", 6))
296 {
297 /*
298 * Basic authentication...
299 */
300
301 httpEncode64_2(encode, sizeof(encode), http->userpass,
302 (int)strlen(http->userpass));
303 snprintf(http->_authstring, sizeof(http->_authstring), "Basic %s", encode);
304 }
305 else
306 {
307 /*
308 * Digest authentication...
309 */
310
311 httpGetSubField(http, HTTP_FIELD_WWW_AUTHENTICATE, "realm", realm);
312 httpGetSubField(http, HTTP_FIELD_WWW_AUTHENTICATE, "nonce", nonce);
313
314 httpMD5(cupsUser(), realm, strchr(http->userpass, ':') + 1, encode);
315 httpMD5Final(nonce, method, resource, encode);
316 snprintf(http->_authstring, sizeof(http->_authstring),
317 "Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", "
318 "uri=\"%s\", response=\"%s\"", cupsUser(), realm, nonce,
319 resource, encode);
320 }
321
322 DEBUG_printf(("cupsDoAuthentication: authstring=\"%s\"\n", http->authstring));
323
324 return (0);
325 }
326
327
328 #ifdef HAVE_GSSAPI
329 # ifdef DEBUG
330 /*
331 * 'DEBUG_gss_printf()' - Show debug error messages from GSSAPI...
332 */
333
334 static void
335 DEBUG_gss_printf(OM_uint32 major_status,/* I - Major status code */
336 OM_uint32 minor_status,/* I - Minor status code */
337 const char *message) /* I - Prefix for error message */
338 {
339 OM_uint32 err_major_status, /* Major status code for display */
340 err_minor_status; /* Minor status code for display */
341 OM_uint32 msg_ctx; /* Message context */
342 gss_buffer_desc major_status_string = GSS_C_EMPTY_BUFFER,
343 /* Major status message */
344 minor_status_string = GSS_C_EMPTY_BUFFER;
345 /* Minor status message */
346
347
348 msg_ctx = 0;
349 err_major_status = gss_display_status(&err_minor_status,
350 major_status,
351 GSS_C_GSS_CODE,
352 GSS_C_NO_OID,
353 &msg_ctx,
354 &major_status_string);
355
356 if (!GSS_ERROR(err_major_status))
357 err_major_status = gss_display_status(&err_minor_status,
358 minor_status,
359 GSS_C_MECH_CODE,
360 GSS_C_NULL_OID,
361 &msg_ctx,
362 &minor_status_string);
363
364 printf("%s: %s, %s\n", message, (char *)major_status_string.value,
365 (char *)minor_status_string.value);
366
367 gss_release_buffer(&err_minor_status, &major_status_string);
368 gss_release_buffer(&err_minor_status, &minor_status_string);
369 }
370 # endif /* DEBUG */
371
372
373 /*
374 * 'cups_get_gss_creds()' - Get CUPS service credentials for authentication.
375 */
376
377 static gss_name_t /* O - Server name */
378 cups_get_gss_creds(
379 http_t *http, /* I - Connection to server */
380 const char *service_name) /* I - Service name */
381 {
382 gss_buffer_desc token = GSS_C_EMPTY_BUFFER;
383 /* Service token */
384 OM_uint32 major_status, /* Major status code */
385 minor_status; /* Minor status code */
386 gss_name_t server_name; /* Server name */
387 char buf[1024], /* Name buffer */
388 fqdn[HTTP_MAX_URI]; /* Server name buffer */
389
390
391 /*
392 * Get a server name we can use for authentication purposes...
393 */
394
395 snprintf(buf, sizeof(buf), "%s@%s", service_name,
396 httpGetHostname(http, fqdn, sizeof(fqdn)));
397
398 token.value = buf;
399 token.length = strlen(buf);
400 server_name = GSS_C_NO_NAME;
401 major_status = gss_import_name(&minor_status, &token,
402 GSS_C_NT_HOSTBASED_SERVICE,
403 &server_name);
404
405 if (GSS_ERROR(major_status))
406 {
407 # ifdef DEBUG
408 DEBUG_gss_printf(major_status, minor_status, "gss_import_name() failed");
409 # endif /* DEBUG */
410
411 return (NULL);
412 }
413
414 return (server_name);
415 }
416 #endif /* HAVE_GSSAPI */
417
418
419 /*
420 * 'cups_local_auth()' - Get the local authorization certificate if
421 * available/applicable...
422 */
423
424 static int /* O - 0 if available */
425 /* 1 if not available */
426 /* -1 error */
427 cups_local_auth(http_t *http) /* I - HTTP connection to server */
428 {
429 #if defined(WIN32) || defined(__EMX__)
430 /*
431 * Currently WIN32 and OS-2 do not support the CUPS server...
432 */
433
434 return (-1);
435 #else
436 int pid; /* Current process ID */
437 FILE *fp; /* Certificate file */
438 char filename[1024], /* Certificate filename */
439 certificate[33];/* Certificate string */
440 _cups_globals_t *cg = _cupsGlobals(); /* Global data */
441 #if defined(HAVE_AUTHORIZATION_H)
442 OSStatus status; /* Status */
443 AuthorizationItem auth_right; /* Authorization right */
444 AuthorizationRights auth_rights; /* Authorization rights */
445 AuthorizationFlags auth_flags; /* Authorization flags */
446 AuthorizationExternalForm auth_extrn; /* Authorization ref external */
447 char auth_key[1024]; /* Buffer */
448 char buffer[1024]; /* Buffer */
449 #endif /* HAVE_AUTHORIZATION_H */
450
451
452 DEBUG_printf(("cups_local_auth(http=%p) hostaddr=%s, hostname=\"%s\"\n",
453 http, httpAddrString(http->hostaddr, filename, sizeof(filename)), http->hostname));
454
455 /*
456 * See if we are accessing localhost...
457 */
458
459 if (!httpAddrLocalhost(http->hostaddr) &&
460 strcasecmp(http->hostname, "localhost") != 0)
461 {
462 DEBUG_puts("cups_local_auth: Not a local connection!");
463 return (-1);
464 }
465
466 #if defined(HAVE_AUTHORIZATION_H)
467 /*
468 * Delete any previous authorization reference...
469 */
470
471 if (cg->auth_ref)
472 {
473 AuthorizationFree(cg->auth_ref, kAuthorizationFlagDefaults);
474 cg->auth_ref = NULL;
475 }
476
477 if (httpGetSubField2(http, HTTP_FIELD_WWW_AUTHENTICATE, "authkey",
478 auth_key, sizeof(auth_key)))
479 {
480 status = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment,
481 kAuthorizationFlagDefaults, &cg->auth_ref);
482 if (status != errAuthorizationSuccess)
483 {
484 DEBUG_printf(("cups_local_auth: AuthorizationCreate() returned %d (%s)\n",
485 (int)status, cssmErrorString(status)));
486 return (-1);
487 }
488
489 auth_right.name = auth_key;
490 auth_right.valueLength = 0;
491 auth_right.value = NULL;
492 auth_right.flags = 0;
493
494 auth_rights.count = 1;
495 auth_rights.items = &auth_right;
496
497 auth_flags = kAuthorizationFlagDefaults |
498 kAuthorizationFlagPreAuthorize |
499 kAuthorizationFlagInteractionAllowed |
500 kAuthorizationFlagExtendRights;
501
502 status = AuthorizationCopyRights(cg->auth_ref, &auth_rights,
503 kAuthorizationEmptyEnvironment,
504 auth_flags, NULL);
505 if (status == errAuthorizationSuccess)
506 status = AuthorizationMakeExternalForm(cg->auth_ref, &auth_extrn);
507
508 if (status == errAuthorizationSuccess)
509 {
510 /*
511 * Set the authorization string and return...
512 */
513
514 httpEncode64_2(buffer, sizeof(buffer), (void *)&auth_extrn,
515 sizeof(auth_extrn));
516
517 http->authstring = malloc(strlen(buffer) + 9);
518 sprintf(http->authstring, "AuthRef %s", buffer);
519
520 /* Copy back to _authstring for backwards compatibility */
521 strlcpy(http->_authstring, http->authstring, sizeof(http->_authstring));
522
523 DEBUG_printf(("cups_local_auth: Returning authstring = \"%s\"\n",
524 http->authstring));
525 return (0);
526 }
527 else if (status == errAuthorizationCanceled)
528 return (-1);
529
530 DEBUG_printf(("cups_local_auth: AuthorizationCopyRights() returned %d (%s)\n",
531 (int)status, cssmErrorString(status)));
532
533 /*
534 * Fall through to try certificates...
535 */
536 }
537 #endif /* HAVE_AUTHORIZATION_H */
538
539 /*
540 * Try opening a certificate file for this PID. If that fails,
541 * try the root certificate...
542 */
543
544 pid = getpid();
545 snprintf(filename, sizeof(filename), "%s/certs/%d", cg->cups_statedir, pid);
546 if ((fp = fopen(filename, "r")) == NULL && pid > 0)
547 {
548 DEBUG_printf(("cups_local_auth: Unable to open file %s: %s\n",
549 filename, strerror(errno)));
550
551 snprintf(filename, sizeof(filename), "%s/certs/0", cg->cups_statedir);
552 fp = fopen(filename, "r");
553 }
554
555 if (fp)
556 {
557 /*
558 * Read the certificate from the file...
559 */
560
561 fgets(certificate, sizeof(certificate), fp);
562 fclose(fp);
563
564 /*
565 * Set the authorization string and return...
566 */
567
568 http->authstring = malloc(strlen(certificate) + 10);
569 sprintf(http->authstring, "Local %s", certificate);
570
571 /* Copy back to _authstring for backwards compatibility */
572 strlcpy(http->_authstring, http->authstring, sizeof(http->_authstring));
573
574 DEBUG_printf(("cups_local_auth: Returning authstring = \"%s\"\n",
575 http->authstring));
576
577 return (0);
578 }
579
580 return (1);
581 #endif /* WIN32 || __EMX__ */
582 }
583
584
585 /*
586 * End of "$Id: auth.c 6397 2007-03-25 23:33:32Z mike $".
587 */