]> 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/*
f7deaa1a 2 * "$Id: auth.c 6253 2007-02-10 18:48:40Z 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
201 if (http->gssname == GSS_C_NO_NAME)
202 {
203 if ((gss_service_name = getenv("CUPS_GSSSERVICENAME")) == NULL)
204 gss_service_name = CUPS_DEFAULT_GSSSERVICENAME;
205 else
206 DEBUG_puts("cupsDoAuthentication: GSS service name set via environment");
207
208 http->gssname = cups_get_gss_creds(http, gss_service_name);
209 }
210
211 /*
212 * Find the start of the Kerberos input token...
213 */
214
215 authorization = httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE);
216
217 authorization += 9;
218 while (*authorization && isspace(*authorization & 255))
219 authorization ++;
220
221 if (*authorization)
222 {
223 /*
224 * For SPNEGO, this is where we'll feed the server's authorization data
225 * back into gss via input_token...
226 */
227 }
228 else
229 {
230 if (http->gssctx != GSS_C_NO_CONTEXT)
231 {
232 major_status = gss_delete_sec_context(&minor_status, &http->gssctx,
233 GSS_C_NO_BUFFER);
234 http->gssctx = GSS_C_NO_CONTEXT;
235 }
236 }
237
238 major_status = gss_init_sec_context(&minor_status, GSS_C_NO_CREDENTIAL,
239 &http->gssctx,
240 http->gssname, http->gssmech,
241 GSS_C_MUTUAL_FLAG, GSS_C_INDEFINITE,
242 GSS_C_NO_CHANNEL_BINDINGS,
243 &input_token, &http->gssmech,
244 &output_token, NULL, NULL);
245
246 if (input_token.value)
247 free(input_token.value);
248
249 if (GSS_ERROR(major_status))
250 {
251# ifdef DEBUG
252 DEBUG_gss_printf(major_status, minor_status,
253 "Unable to initialise security context");
254# endif /* DEBUG */
255 return (-1);
256 }
257
258# ifdef DEBUG
259 if (major_status == GSS_S_CONTINUE_NEEDED)
260 DEBUG_gss_printf(major_status, minor_status, "Continuation needed!");
261# endif /* DEBUG */
262
263 if (output_token.length)
264 {
265 httpEncode64_2(encode, sizeof(encode), output_token.value,
266 output_token.length);
267
268 http->authstring = malloc(strlen(encode) + 11);
269 sprintf(http->authstring, "Negotiate %s", encode); /* Safe because allocated */
270
271 major_status = gss_release_buffer(&minor_status, &output_token);
272 }
273
274 /*
275 * Copy back what we can to _authstring for backwards compatibility...
276 */
277
278 strlcpy(http->_authstring, http->authstring, sizeof(http->_authstring));
279#endif /* HAVE_GSSAPI */
280 }
281 else if (strncmp(http->fields[HTTP_FIELD_WWW_AUTHENTICATE], "Digest", 6))
ef416fc2 282 {
283 /*
284 * Basic authentication...
285 */
286
287 httpEncode64_2(encode, sizeof(encode), http->userpass,
b86bc4cf 288 (int)strlen(http->userpass));
f7deaa1a 289 snprintf(http->_authstring, sizeof(http->_authstring), "Basic %s", encode);
ef416fc2 290 }
291 else
292 {
293 /*
294 * Digest authentication...
295 */
296
297 httpGetSubField(http, HTTP_FIELD_WWW_AUTHENTICATE, "realm", realm);
298 httpGetSubField(http, HTTP_FIELD_WWW_AUTHENTICATE, "nonce", nonce);
299
300 httpMD5(cupsUser(), realm, strchr(http->userpass, ':') + 1, encode);
301 httpMD5Final(nonce, method, resource, encode);
f7deaa1a 302 snprintf(http->_authstring, sizeof(http->_authstring),
ef416fc2 303 "Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", "
304 "uri=\"%s\", response=\"%s\"", cupsUser(), realm, nonce,
305 resource, encode);
306 }
307
308 DEBUG_printf(("cupsDoAuthentication: authstring=\"%s\"\n", http->authstring));
309
310 return (0);
311}
312
313
f7deaa1a 314#ifdef HAVE_GSSAPI
315# ifdef DEBUG
316/*
317 * 'DEBUG_gss_printf()' - Show debug error messages from GSSAPI...
318 */
319
320static void
321DEBUG_gss_printf(OM_uint32 major_status,/* I - Major status code */
322 OM_uint32 minor_status,/* I - Minor status code */
323 const char *message) /* I - Prefix for error message */
324{
325 OM_uint32 err_major_status, /* Major status code for display */
326 err_minor_status; /* Minor status code for display */
327 OM_uint32 msg_ctx; /* Message context */
328 gss_buffer_desc major_status_string = GSS_C_EMPTY_BUFFER,
329 /* Major status message */
330 minor_status_string = GSS_C_EMPTY_BUFFER;
331 /* Minor status message */
332
333
334 msg_ctx = 0;
335 err_major_status = gss_display_status(&err_minor_status,
336 major_status,
337 GSS_C_GSS_CODE,
338 GSS_C_NO_OID,
339 &msg_ctx,
340 &major_status_string);
341
342 if (!GSS_ERROR(err_major_status))
343 err_major_status = gss_display_status(&err_minor_status,
344 minor_status,
345 GSS_C_MECH_CODE,
346 GSS_C_NULL_OID,
347 &msg_ctx,
348 &minor_status_string);
349
350 printf("%s: %s, %s\n", message, (char *)major_status_string.value,
351 (char *)minor_status_string.value);
352
353 gss_release_buffer(&err_minor_status, &major_status_string);
354 gss_release_buffer(&err_minor_status, &minor_status_string);
355}
356# endif /* DEBUG */
357
358
359/*
360 * 'cups_get_gss_creds()' - Get CUPS service credentials for authentication.
361 */
362
363static gss_name_t /* O - Server name */
364cups_get_gss_creds(
365 http_t *http, /* I - Connection to server */
366 const char *service_name) /* I - Service name */
367{
368 gss_buffer_desc token = GSS_C_EMPTY_BUFFER;
369 /* Service token */
370 OM_uint32 major_status, /* Major status code */
371 minor_status; /* Minor status code */
372 gss_name_t server_name; /* Server name */
373 char buf[1024], /* Name buffer */
374 fqdn[HTTP_MAX_URI]; /* Server name buffer */
375
376
377 /*
378 * Get a server name we can use for authentication purposes...
379 */
380
381 snprintf(buf, sizeof(buf), "%s@%s", service_name,
382 httpGetHostname(http, fqdn, sizeof(fqdn)));
383
384 token.value = buf;
385 token.length = strlen(buf);
386 server_name = GSS_C_NO_NAME;
387 major_status = gss_import_name(&minor_status, &token,
388 GSS_C_NT_HOSTBASED_SERVICE,
389 &server_name);
390
391 if (GSS_ERROR(major_status))
392 {
393# ifdef DEBUG
394 DEBUG_gss_printf(major_status, minor_status, "gss_import_name() failed");
395# endif /* DEBUG */
396
397 return (NULL);
398 }
399
400 return (server_name);
401}
402#endif /* HAVE_GSSAPI */
403
404
ef416fc2 405/*
406 * 'cups_local_auth()' - Get the local authorization certificate if
407 * available/applicable...
408 */
409
f7deaa1a 410static int /* O - 0 if available */
411 /* 1 if not available */
412 /* -1 error */
ef416fc2 413cups_local_auth(http_t *http) /* I - HTTP connection to server */
414{
415#if defined(WIN32) || defined(__EMX__)
416 /*
417 * Currently WIN32 and OS-2 do not support the CUPS server...
418 */
419
420 return (-1);
421#else
f7deaa1a 422 int pid; /* Current process ID */
423 FILE *fp; /* Certificate file */
424 char filename[1024], /* Certificate filename */
425 certificate[33];/* Certificate string */
ef416fc2 426 _cups_globals_t *cg = _cupsGlobals(); /* Global data */
f7deaa1a 427#if defined(HAVE_AUTHORIZATION_H)
428 OSStatus status; /* Status */
429 AuthorizationItem auth_right; /* Authorization right */
430 AuthorizationRights auth_rights; /* Authorization rights */
431 AuthorizationFlags auth_flags; /* Authorization flags */
432 AuthorizationExternalForm auth_extrn; /* Authorization ref external */
433 char auth_key[1024]; /* Buffer */
434 char buffer[1024]; /* Buffer */
435#endif /* HAVE_AUTHORIZATION_H */
ef416fc2 436
437
438 DEBUG_printf(("cups_local_auth(http=%p) hostaddr=%s, hostname=\"%s\"\n",
439 http, httpAddrString(http->hostaddr, filename, sizeof(filename)), http->hostname));
440
441 /*
442 * See if we are accessing localhost...
443 */
444
445 if (!httpAddrLocalhost(http->hostaddr) &&
446 strcasecmp(http->hostname, "localhost") != 0)
447 {
448 DEBUG_puts("cups_local_auth: Not a local connection!");
449 return (-1);
450 }
451
f7deaa1a 452#if defined(HAVE_AUTHORIZATION_H)
453 /*
454 * Delete any previous authorization reference...
455 */
456
457 if (cg->auth_ref)
458 {
459 AuthorizationFree(cg->auth_ref, kAuthorizationFlagDefaults);
460 cg->auth_ref = NULL;
461 }
462
463 if (httpGetSubField2(http, HTTP_FIELD_WWW_AUTHENTICATE, "authkey",
464 auth_key, sizeof(auth_key)))
465 {
466 status = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment,
467 kAuthorizationFlagDefaults, &cg->auth_ref);
468 if (status != errAuthorizationSuccess)
469 {
470 DEBUG_printf(("cups_local_auth: AuthorizationCreate() returned %d (%s)\n",
471 (int)status, cssmErrorString(status)));
472 return (-1);
473 }
474
475 auth_right.name = auth_key;
476 auth_right.valueLength = 0;
477 auth_right.value = NULL;
478 auth_right.flags = 0;
479
480 auth_rights.count = 1;
481 auth_rights.items = &auth_right;
482
483 auth_flags = kAuthorizationFlagDefaults |
484 kAuthorizationFlagPreAuthorize |
485 kAuthorizationFlagInteractionAllowed |
486 kAuthorizationFlagExtendRights;
487
488 status = AuthorizationCopyRights(cg->auth_ref, &auth_rights,
489 kAuthorizationEmptyEnvironment,
490 auth_flags, NULL);
491 if (status == errAuthorizationSuccess)
492 status = AuthorizationMakeExternalForm(cg->auth_ref, &auth_extrn);
493
494 if (status == errAuthorizationSuccess)
495 {
496 /*
497 * Set the authorization string and return...
498 */
499
500 httpEncode64_2(buffer, sizeof(buffer), (void *)&auth_extrn,
501 sizeof(auth_extrn));
502
503 http->authstring = malloc(strlen(buffer) + 9);
504 sprintf(http->authstring, "AuthRef %s", buffer);
505
506 /* Copy back to _authstring for backwards compatibility */
507 strlcpy(http->_authstring, http->authstring, sizeof(http->_authstring));
508
509 DEBUG_printf(("cups_local_auth: Returning authstring = \"%s\"\n",
510 http->authstring));
511 return (0);
512 }
513 else if (status == errAuthorizationCanceled)
514 return (-1);
515
516 DEBUG_printf(("cups_local_auth: AuthorizationCopyRights() returned %d (%s)\n",
517 (int)status, cssmErrorString(status)));
518
519 /*
520 * Fall through to try certificates...
521 */
522 }
523#endif /* HAVE_AUTHORIZATION_H */
524
ef416fc2 525 /*
526 * Try opening a certificate file for this PID. If that fails,
527 * try the root certificate...
528 */
529
530 pid = getpid();
531 snprintf(filename, sizeof(filename), "%s/certs/%d", cg->cups_statedir, pid);
532 if ((fp = fopen(filename, "r")) == NULL && pid > 0)
533 {
534 DEBUG_printf(("cups_local_auth: Unable to open file %s: %s\n",
535 filename, strerror(errno)));
536
537 snprintf(filename, sizeof(filename), "%s/certs/0", cg->cups_statedir);
538 fp = fopen(filename, "r");
539 }
540
f7deaa1a 541 if (fp)
ef416fc2 542 {
f7deaa1a 543 /*
544 * Read the certificate from the file...
545 */
ef416fc2 546
f7deaa1a 547 fgets(certificate, sizeof(certificate), fp);
548 fclose(fp);
ef416fc2 549
f7deaa1a 550 /*
551 * Set the authorization string and return...
552 */
ef416fc2 553
f7deaa1a 554 http->authstring = malloc(strlen(certificate) + 10);
555 sprintf(http->authstring, "Local %s", certificate);
ef416fc2 556
f7deaa1a 557 /* Copy back to _authstring for backwards compatibility */
558 strlcpy(http->_authstring, http->authstring, sizeof(http->_authstring));
ef416fc2 559
f7deaa1a 560 DEBUG_printf(("cups_local_auth: Returning authstring = \"%s\"\n",
561 http->authstring));
ef416fc2 562
f7deaa1a 563 return (0);
564 }
565
566 return (1);
ef416fc2 567#endif /* WIN32 || __EMX__ */
568}
569
570
571/*
f7deaa1a 572 * End of "$Id: auth.c 6253 2007-02-10 18:48:40Z mike $".
ef416fc2 573 */