]> git.ipfire.org Git - thirdparty/cups.git/blame - cups/auth.c
Load cups into easysw/current.
[thirdparty/cups.git] / cups / auth.c
CommitLineData
ef416fc2 1/*
b94498cf 2 * "$Id: auth.c 6499 2007-04-30 21:44:43Z mike $"
ef416fc2 3 *
4 * Authentication functions for the Common UNIX Printing System (CUPS).
5 *
b86bc4cf 6 * Copyright 1997-2007 by Easy Software Products.
ef416fc2 7 *
f7deaa1a 8 * This file contains Kerberos support code, copyright 2006 by
9 * Jelmer Vernooij.
10 *
ef416fc2 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.
f7deaa1a 32 * DEBUG_gss_printf() - Show debug error messages from GSSAPI...
33 * cups_get_gss_creds() - Get CUPS service credentials for authentication.
ef416fc2 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
f7deaa1a 55#if HAVE_AUTHORIZATION_H
56# include <Security/Authorization.h>
57# ifdef HAVE_SECBASEPRIV_H
58# include <Security/SecBasePriv.h>
59# else
60extern const char *cssmErrorString(int error);
61# endif /* HAVE_SECBASEPRIV_H */
62#endif /* HAVE_AUTHORIZATION_H */
63
ef416fc2 64
65/*
66 * Local functions...
67 */
68
f7deaa1a 69#ifdef HAVE_GSSAPI
70# ifdef DEBUG
71static void DEBUG_gss_printf(OM_uint32 major_status, OM_uint32 minor_status,
72 const char *message);
73# endif /* DEBUG */
74static gss_name_t cups_get_gss_creds(http_t *http, const char *service_name);
75#endif /* HAVE_GSSAPI */
ef416fc2 76static 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
88int /* O - 0 on success, -1 on error */
89cupsDoAuthentication(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 */
f7deaa1a 98 encode[2048]; /* Encoded username:password */
99 int localauth; /* Local authentication result */
b86bc4cf 100 _cups_globals_t *cg; /* Global data */
ef416fc2 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));
07725fee 107 DEBUG_printf(("cupsDoAuthentication: WWW-Authenticate=\"%s\"\n",
108 httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE)));
ef416fc2 109
110 /*
111 * Clear the current authentication string...
112 */
113
f7deaa1a 114 http->_authstring[0] = '\0';
115
116 if (http->authstring && http->authstring != http->_authstring)
117 free(http->authstring);
118
119 http->authstring = http->_authstring;
ef416fc2 120
121 /*
122 * See if we can do local authentication...
123 */
124
f7deaa1a 125 if (http->digest_tries < 3)
ef416fc2 126 {
f7deaa1a 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 */
ef416fc2 139 }
140
141 /*
142 * Nope, see if we should retry the current username:password...
143 */
144
f7deaa1a 145 if ((http->digest_tries > 1 || !http->userpass[0]) &&
146 strncmp(http->fields[HTTP_FIELD_WWW_AUTHENTICATE], "Negotiate", 9))
ef416fc2 147 {
148 /*
149 * Nope - get a new password from the user...
150 */
151
b86bc4cf 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);
ef416fc2 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
f7deaa1a 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
f42414bf 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
f7deaa1a 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))
ef416fc2 296 {
297 /*
298 * Basic authentication...
299 */
300
301 httpEncode64_2(encode, sizeof(encode), http->userpass,
b86bc4cf 302 (int)strlen(http->userpass));
f7deaa1a 303 snprintf(http->_authstring, sizeof(http->_authstring), "Basic %s", encode);
ef416fc2 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);
f7deaa1a 316 snprintf(http->_authstring, sizeof(http->_authstring),
ef416fc2 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
f7deaa1a 328#ifdef HAVE_GSSAPI
329# ifdef DEBUG
330/*
331 * 'DEBUG_gss_printf()' - Show debug error messages from GSSAPI...
332 */
333
334static void
335DEBUG_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
377static gss_name_t /* O - Server name */
378cups_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
ef416fc2 419/*
420 * 'cups_local_auth()' - Get the local authorization certificate if
421 * available/applicable...
422 */
423
f7deaa1a 424static int /* O - 0 if available */
425 /* 1 if not available */
426 /* -1 error */
ef416fc2 427cups_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
f7deaa1a 436 int pid; /* Current process ID */
437 FILE *fp; /* Certificate file */
438 char filename[1024], /* Certificate filename */
439 certificate[33];/* Certificate string */
ef416fc2 440 _cups_globals_t *cg = _cupsGlobals(); /* Global data */
f7deaa1a 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 */
ef416fc2 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
f7deaa1a 466#if defined(HAVE_AUTHORIZATION_H)
467 /*
468 * Delete any previous authorization reference...
469 */
470
b94498cf 471 if (http->auth_ref)
f7deaa1a 472 {
b94498cf 473 AuthorizationFree(http->auth_ref, kAuthorizationFlagDefaults);
474 http->auth_ref = NULL;
f7deaa1a 475 }
476
477 if (httpGetSubField2(http, HTTP_FIELD_WWW_AUTHENTICATE, "authkey",
478 auth_key, sizeof(auth_key)))
479 {
480 status = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment,
b94498cf 481 kAuthorizationFlagDefaults, &http->auth_ref);
f7deaa1a 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
b94498cf 502 status = AuthorizationCopyRights(http->auth_ref, &auth_rights,
f7deaa1a 503 kAuthorizationEmptyEnvironment,
504 auth_flags, NULL);
505 if (status == errAuthorizationSuccess)
b94498cf 506 status = AuthorizationMakeExternalForm(http->auth_ref, &auth_extrn);
f7deaa1a 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
ef416fc2 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
f7deaa1a 555 if (fp)
ef416fc2 556 {
f7deaa1a 557 /*
558 * Read the certificate from the file...
559 */
ef416fc2 560
f7deaa1a 561 fgets(certificate, sizeof(certificate), fp);
562 fclose(fp);
ef416fc2 563
f7deaa1a 564 /*
565 * Set the authorization string and return...
566 */
ef416fc2 567
f7deaa1a 568 http->authstring = malloc(strlen(certificate) + 10);
569 sprintf(http->authstring, "Local %s", certificate);
ef416fc2 570
f7deaa1a 571 /* Copy back to _authstring for backwards compatibility */
572 strlcpy(http->_authstring, http->authstring, sizeof(http->_authstring));
ef416fc2 573
f7deaa1a 574 DEBUG_printf(("cups_local_auth: Returning authstring = \"%s\"\n",
575 http->authstring));
ef416fc2 576
f7deaa1a 577 return (0);
578 }
579
580 return (1);
ef416fc2 581#endif /* WIN32 || __EMX__ */
582}
583
584
585/*
b94498cf 586 * End of "$Id: auth.c 6499 2007-04-30 21:44:43Z mike $".
ef416fc2 587 */