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