]> git.ipfire.org Git - thirdparty/cups.git/blame - cups/auth.c
Changelog.
[thirdparty/cups.git] / cups / auth.c
CommitLineData
ef416fc2 1/*
7e86f2f6 2 * Authentication functions for CUPS.
ef416fc2 3 *
fa26ab95 4 * Copyright 2007-2019 by Apple Inc.
7e86f2f6 5 * Copyright 1997-2007 by Easy Software Products.
ef416fc2 6 *
7e86f2f6
MS
7 * This file contains Kerberos support code, copyright 2006 by
8 * Jelmer Vernooij.
f7deaa1a 9 *
e3101897 10 * Licensed under Apache License v2.0. See the file "LICENSE" for more information.
ef416fc2 11 */
12
13/*
14 * Include necessary headers...
15 */
16
71e16022 17#include "cups-private.h"
fb863569 18#include "debug-internal.h"
ef416fc2 19#include <fcntl.h>
20#include <sys/stat.h>
19dc16f7 21#if defined(_WIN32) || defined(__EMX__)
ef416fc2 22# include <io.h>
23#else
24# include <unistd.h>
19dc16f7 25#endif /* _WIN32 || __EMX__ */
ef416fc2 26
f7deaa1a 27#if HAVE_AUTHORIZATION_H
28# include <Security/Authorization.h>
f7deaa1a 29#endif /* HAVE_AUTHORIZATION_H */
30
bc44d920 31#if defined(SO_PEERCRED) && defined(AF_LOCAL)
32# include <pwd.h>
33#endif /* SO_PEERCRED && AF_LOCAL */
34
ef416fc2 35
36/*
37 * Local functions...
38 */
39
7ec11630
MS
40static const char *cups_auth_find(const char *www_authenticate, const char *scheme);
41static const char *cups_auth_param(const char *scheme, const char *name, char *value, size_t valsize);
42static const char *cups_auth_scheme(const char *www_authenticate, char *scheme, size_t schemesize);
43
f7deaa1a 44#ifdef HAVE_GSSAPI
eac3a0a0
MS
45# ifdef HAVE_GSS_ACQUIRE_CRED_EX_F
46# ifdef HAVE_GSS_GSSAPI_SPI_H
47# include <GSS/gssapi_spi.h>
48# else
a2326b5b
MS
49# define GSS_AUTH_IDENTITY_TYPE_1 1
50# define gss_acquire_cred_ex_f __ApplePrivate_gss_acquire_cred_ex_f
53af7f21 51typedef struct gss_auth_identity /* @private@ */
eac3a0a0
MS
52{
53 uint32_t type;
54 uint32_t flags;
55 char *username;
56 char *realm;
57 char *password;
58 gss_buffer_t *credentialsRef;
49c59293 59} gss_auth_identity_desc;
eac3a0a0
MS
60extern OM_uint32 gss_acquire_cred_ex_f(gss_status_id_t, const gss_name_t,
61 OM_uint32, OM_uint32, const gss_OID,
62 gss_cred_usage_t, gss_auth_identity_t,
63 void *, void (*)(void *, OM_uint32,
64 gss_status_id_t,
65 gss_cred_id_t,
66 gss_OID_set,
67 OM_uint32));
68# endif /* HAVE_GSS_GSSAPI_SPI_H */
69# include <dispatch/dispatch.h>
70typedef struct _cups_gss_acquire_s /* Acquire callback data */
71{
72 dispatch_semaphore_t sem; /* Synchronization semaphore */
73 OM_uint32 major; /* Returned status code */
74 gss_cred_id_t creds; /* Returned credentials */
75} _cups_gss_acquire_t;
76
77static void cups_gss_acquire(void *ctx, OM_uint32 major,
78 gss_status_id_t status,
79 gss_cred_id_t creds, gss_OID_set oids,
80 OM_uint32 time_rec);
81# endif /* HAVE_GSS_ACQUIRE_CRED_EX_F */
82static gss_name_t cups_gss_getname(http_t *http, const char *service_name);
f7deaa1a 83# ifdef DEBUG
e07d4801
MS
84static void cups_gss_printf(OM_uint32 major_status, OM_uint32 minor_status,
85 const char *message);
355e94dc 86# else
e07d4801
MS
87# define cups_gss_printf(major, minor, message)
88# endif /* DEBUG */
f7deaa1a 89#endif /* HAVE_GSSAPI */
ef416fc2 90static int cups_local_auth(http_t *http);
91
92
93/*
94 * 'cupsDoAuthentication()' - Authenticate a request.
95 *
cb7f98ee 96 * This function should be called in response to a @code HTTP_STATUS_UNAUTHORIZED@
ef416fc2 97 * status, prior to resubmitting your request.
98 *
8072030b 99 * @since CUPS 1.1.20/macOS 10.4@
ef416fc2 100 */
101
102int /* O - 0 on success, -1 on error */
f11a948a
MS
103cupsDoAuthentication(
104 http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
105 const char *method, /* I - Request method ("GET", "POST", "PUT") */
106 const char *resource) /* I - Resource path */
ef416fc2 107{
3e7fe0ca 108 const char *password, /* Password string */
7ec11630
MS
109 *www_auth, /* WWW-Authenticate header */
110 *schemedata; /* Scheme-specific data */
111 char scheme[256], /* Scheme name */
16f67389 112 prompt[1024]; /* Prompt for user */
f7deaa1a 113 int localauth; /* Local authentication result */
b86bc4cf 114 _cups_globals_t *cg; /* Global data */
ef416fc2 115
116
807315e6 117 DEBUG_printf(("cupsDoAuthentication(http=%p, method=\"%s\", resource=\"%s\")", (void *)http, method, resource));
ef416fc2 118
f11a948a
MS
119 if (!http)
120 http = _cupsConnect();
121
122 if (!http || !method || !resource)
123 return (-1);
124
5a6b583a
MS
125 DEBUG_printf(("2cupsDoAuthentication: digest_tries=%d, userpass=\"%s\"",
126 http->digest_tries, http->userpass));
127 DEBUG_printf(("2cupsDoAuthentication: WWW-Authenticate=\"%s\"",
128 httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE)));
129
ef416fc2 130 /*
131 * Clear the current authentication string...
132 */
133
355e94dc 134 httpSetAuthString(http, NULL, NULL);
ef416fc2 135
136 /*
137 * See if we can do local authentication...
138 */
139
f7deaa1a 140 if (http->digest_tries < 3)
ef416fc2 141 {
f7deaa1a 142 if ((localauth = cups_local_auth(http)) == 0)
143 {
e07d4801 144 DEBUG_printf(("2cupsDoAuthentication: authstring=\"%s\"",
f7deaa1a 145 http->authstring));
f14324a7 146
cb7f98ee 147 if (http->status == HTTP_STATUS_UNAUTHORIZED)
f7deaa1a 148 http->digest_tries ++;
f14324a7 149
f7deaa1a 150 return (0);
151 }
152 else if (localauth == -1)
f11a948a 153 {
cb7f98ee 154 http->status = HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED;
f7deaa1a 155 return (-1); /* Error or canceled */
f11a948a 156 }
ef416fc2 157 }
158
159 /*
7ec11630 160 * Nope, loop through the authentication schemes to find the first we support.
ef416fc2 161 */
162
7ec11630 163 www_auth = httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE);
3e7fe0ca 164
7ec11630 165 for (schemedata = cups_auth_scheme(www_auth, scheme, sizeof(scheme)); schemedata; schemedata = cups_auth_scheme(schemedata + strlen(scheme), scheme, sizeof(scheme)))
ef416fc2 166 {
167 /*
7ec11630 168 * Check the scheme name...
ef416fc2 169 */
170
7ec11630
MS
171#ifdef HAVE_GSSAPI
172 if (!_cups_strcasecmp(scheme, "Negotiate"))
173 {
174 /*
175 * Kerberos authentication...
176 */
5a9febac 177
7ec11630
MS
178 if (_cupsSetNegotiateAuthString(http, method, resource))
179 {
180 http->status = HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED;
181 return (-1);
182 }
b86bc4cf 183
7ec11630
MS
184 break;
185 }
186 else
187#endif /* HAVE_GSSAPI */
188 if (_cups_strcasecmp(scheme, "Basic") && _cups_strcasecmp(scheme, "Digest"))
189 continue; /* Not supported (yet) */
b86bc4cf 190
7ec11630
MS
191 /*
192 * See if we should retry the current username:password...
193 */
5a9febac 194
7ec11630
MS
195 if ((http->digest_tries > 1 || !http->userpass[0]) && (!_cups_strcasecmp(scheme, "Basic") || (!_cups_strcasecmp(scheme, "Digest"))))
196 {
197 /*
198 * Nope - get a new password from the user...
199 */
ef416fc2 200
7ec11630
MS
201 char default_username[HTTP_MAX_VALUE];
202 /* Default username */
ef416fc2 203
7ec11630 204 cg = _cupsGlobals();
ef416fc2 205
7ec11630
MS
206 if (!cg->lang_default)
207 cg->lang_default = cupsLangDefault();
ef416fc2 208
44cb0dd2 209 if (cups_auth_param(schemedata, "username", default_username, sizeof(default_username)))
7ec11630 210 cupsSetUser(default_username);
5a6b583a 211
7ec11630 212 snprintf(prompt, sizeof(prompt), _cupsLangString(cg->lang_default, _("Password for %s on %s? ")), cupsUser(), http->hostname[0] == '/' ? "localhost" : http->hostname);
5a6b583a 213
7ec11630
MS
214 http->digest_tries = _cups_strncasecmp(scheme, "Digest", 6) != 0;
215 http->userpass[0] = '\0';
ef416fc2 216
7ec11630
MS
217 if ((password = cupsGetPassword2(prompt, http, method, resource)) == NULL)
218 {
219 http->status = HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED;
220 return (-1);
221 }
f7deaa1a 222
7ec11630
MS
223 snprintf(http->userpass, sizeof(http->userpass), "%s:%s", cupsUser(), password);
224 }
225 else if (http->status == HTTP_STATUS_UNAUTHORIZED)
226 http->digest_tries ++;
227
228 if (http->status == HTTP_STATUS_UNAUTHORIZED && http->digest_tries >= 3)
f7deaa1a 229 {
7ec11630
MS
230 DEBUG_printf(("1cupsDoAuthentication: Too many authentication tries (%d)", http->digest_tries));
231
cb7f98ee 232 http->status = HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED;
f7deaa1a 233 return (-1);
234 }
7ec11630 235
ef416fc2 236 /*
7ec11630 237 * Got a password; encode it for the server...
ef416fc2 238 */
239
7ec11630
MS
240 if (!_cups_strcasecmp(scheme, "Basic"))
241 {
242 /*
243 * Basic authentication...
244 */
5a738aea 245
7ec11630 246 char encode[256]; /* Base64 buffer */
5a738aea 247
7ec11630
MS
248 httpEncode64_2(encode, sizeof(encode), http->userpass, (int)strlen(http->userpass));
249 httpSetAuthString(http, "Basic", encode);
16f67389 250 break;
7ec11630
MS
251 }
252 else if (!_cups_strcasecmp(scheme, "Digest"))
253 {
254 /*
255 * Digest authentication...
256 */
257
16f67389
MS
258 char nonce[HTTP_MAX_VALUE]; /* nonce="xyz" string */
259
260 cups_auth_param(schemedata, "algorithm", http->algorithm, sizeof(http->algorithm));
261 cups_auth_param(schemedata, "opaque", http->opaque, sizeof(http->opaque));
262 cups_auth_param(schemedata, "nonce", nonce, sizeof(nonce));
263 cups_auth_param(schemedata, "realm", http->realm, sizeof(http->realm));
7ec11630 264
5737d5eb 265 if (_httpSetDigestAuthString(http, nonce, method, resource))
16f67389 266 break;
7ec11630
MS
267 }
268 }
269
270 if (http->authstring)
271 {
272 DEBUG_printf(("1cupsDoAuthentication: authstring=\"%s\"", http->authstring));
273
274 return (0);
ef416fc2 275 }
f14324a7
MS
276 else
277 {
7ec11630 278 DEBUG_printf(("1cupsDoAuthentication: Unknown auth type: \"%s\"", www_auth));
cb7f98ee 279 http->status = HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED;
7ec11630 280
f14324a7
MS
281 return (-1);
282 }
ef416fc2 283}
284
285
f7deaa1a 286#ifdef HAVE_GSSAPI
f14324a7
MS
287/*
288 * '_cupsSetNegotiateAuthString()' - Set the Kerberos authentication string.
289 */
290
291int /* O - 0 on success, -1 on error */
292_cupsSetNegotiateAuthString(
eac3a0a0
MS
293 http_t *http, /* I - Connection to server */
294 const char *method, /* I - Request method ("GET", "POST", "PUT") */
295 const char *resource) /* I - Resource path */
f14324a7
MS
296{
297 OM_uint32 minor_status, /* Minor status code */
298 major_status; /* Major status code */
299 gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
07ed0e9a 300 /* Output token */
f14324a7
MS
301
302
321d8d57
MS
303 (void)method;
304 (void)resource;
305
f14324a7
MS
306# ifdef __APPLE__
307 /*
308 * If the weak-linked GSSAPI/Kerberos library is not present, don't try
309 * to use it...
310 */
311
6d8021f4 312 if (&gss_init_sec_context == NULL)
f14324a7
MS
313 {
314 DEBUG_puts("1_cupsSetNegotiateAuthString: Weak-linked GSSAPI/Kerberos "
315 "framework is not present");
316 return (-1);
317 }
318# endif /* __APPLE__ */
319
320 if (http->gssname == GSS_C_NO_NAME)
321 {
eac3a0a0 322 http->gssname = cups_gss_getname(http, _cupsGSSServiceName());
f14324a7
MS
323 }
324
325 if (http->gssctx != GSS_C_NO_CONTEXT)
326 {
327 gss_delete_sec_context(&minor_status, &http->gssctx, GSS_C_NO_BUFFER);
328 http->gssctx = GSS_C_NO_CONTEXT;
329 }
330
eac3a0a0
MS
331 major_status = gss_init_sec_context(&minor_status, GSS_C_NO_CREDENTIAL,
332 &http->gssctx,
333 http->gssname, http->gssmech,
334 GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG,
335 GSS_C_INDEFINITE,
336 GSS_C_NO_CHANNEL_BINDINGS,
337 GSS_C_NO_BUFFER, &http->gssmech,
338 &output_token, NULL, NULL);
339
accedcf8 340# ifdef HAVE_GSS_ACQUIRE_CRED_EX_F
eac3a0a0
MS
341 if (major_status == GSS_S_NO_CRED)
342 {
343 /*
344 * Ask the user for credentials...
345 */
346
347 char prompt[1024], /* Prompt for user */
348 userbuf[256]; /* Kerberos username */
349 const char *username, /* Username string */
350 *password; /* Password string */
351 _cups_gss_acquire_t data; /* Callback data */
49c59293 352 gss_auth_identity_desc identity; /* Kerberos user identity */
eac3a0a0
MS
353 _cups_globals_t *cg = _cupsGlobals();
354 /* Per-thread global data */
355
356 if (!cg->lang_default)
357 cg->lang_default = cupsLangDefault();
358
359 snprintf(prompt, sizeof(prompt),
360 _cupsLangString(cg->lang_default, _("Password for %s on %s? ")),
f228370c 361 cupsUser(), http->gsshost);
eac3a0a0
MS
362
363 if ((password = cupsGetPassword2(prompt, http, method, resource)) == NULL)
364 return (-1);
365
366 /*
367 * Try to acquire credentials...
368 */
369
370 username = cupsUser();
371 if (!strchr(username, '@'))
372 {
f228370c 373 snprintf(userbuf, sizeof(userbuf), "%s@%s", username, http->gsshost);
eac3a0a0
MS
374 username = userbuf;
375 }
376
377 identity.type = GSS_AUTH_IDENTITY_TYPE_1;
378 identity.flags = 0;
379 identity.username = (char *)username;
380 identity.realm = (char *)"";
381 identity.password = (char *)password;
382 identity.credentialsRef = NULL;
383
384 data.sem = dispatch_semaphore_create(0);
385 data.major = 0;
386 data.creds = NULL;
387
388 if (data.sem)
389 {
5dcbe84d 390 major_status = gss_acquire_cred_ex_f(NULL, GSS_C_NO_NAME, 0, GSS_C_INDEFINITE, GSS_KRB5_MECHANISM, GSS_C_INITIATE, (gss_auth_identity_t)&identity, &data, cups_gss_acquire);
eac3a0a0
MS
391
392 if (major_status == GSS_S_COMPLETE)
393 {
394 dispatch_semaphore_wait(data.sem, DISPATCH_TIME_FOREVER);
395 major_status = data.major;
396 }
397
398 dispatch_release(data.sem);
399
400 if (major_status == GSS_S_COMPLETE)
401 {
402 OM_uint32 release_minor; /* Minor status from releasing creds */
403
404 major_status = gss_init_sec_context(&minor_status, data.creds,
405 &http->gssctx,
406 http->gssname, http->gssmech,
407 GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG,
408 GSS_C_INDEFINITE,
409 GSS_C_NO_CHANNEL_BINDINGS,
410 GSS_C_NO_BUFFER, &http->gssmech,
411 &output_token, NULL, NULL);
412 gss_release_cred(&release_minor, &data.creds);
413 }
414 }
415 }
accedcf8 416# endif /* HAVE_GSS_ACQUIRED_CRED_EX_F */
f14324a7
MS
417
418 if (GSS_ERROR(major_status))
419 {
420 cups_gss_printf(major_status, minor_status,
421 "_cupsSetNegotiateAuthString: Unable to initialize "
422 "security context");
423 return (-1);
424 }
425
accedcf8 426# ifdef DEBUG
eac3a0a0 427 else if (major_status == GSS_S_CONTINUE_NEEDED)
f14324a7
MS
428 cups_gss_printf(major_status, minor_status,
429 "_cupsSetNegotiateAuthString: Continuation needed!");
accedcf8 430# endif /* DEBUG */
f14324a7
MS
431
432 if (output_token.length > 0 && output_token.length <= 65536)
433 {
434 /*
435 * Allocate the authorization string since Windows KDCs can have
436 * arbitrarily large credentials...
437 */
438
7e86f2f6 439 int authsize = 10 + /* "Negotiate " */
cee8cdad 440 (((int)output_token.length * 4 / 3 + 3) & ~3) + 1;
7e86f2f6 441 /* Base64 + nul */
f14324a7
MS
442
443 httpSetAuthString(http, NULL, NULL);
444
7e86f2f6 445 if ((http->authstring = malloc((size_t)authsize)) == NULL)
f14324a7
MS
446 {
447 http->authstring = http->_authstring;
448 authsize = sizeof(http->_authstring);
449 }
450
07623986 451 strlcpy(http->authstring, "Negotiate ", (size_t)authsize);
f14324a7 452 httpEncode64_2(http->authstring + 10, authsize - 10, output_token.value,
7e86f2f6 453 (int)output_token.length);
f14324a7
MS
454
455 gss_release_buffer(&minor_status, &output_token);
456 }
457 else
458 {
459 DEBUG_printf(("1_cupsSetNegotiateAuthString: Kerberos credentials too "
460 "large - %d bytes!", (int)output_token.length));
461 gss_release_buffer(&minor_status, &output_token);
462
463 return (-1);
464 }
465
466 return (0);
467}
accedcf8 468#endif /* HAVE_GSSAPI */
f14324a7
MS
469
470
7ec11630
MS
471/*
472 * 'cups_auth_find()' - Find the named WWW-Authenticate scheme.
473 *
474 * The "www_authenticate" parameter points to the current position in the header.
475 *
476 * Returns @code NULL@ if the auth scheme is not present.
477 */
478
479static const char * /* O - Start of matching scheme or @code NULL@ if not found */
480cups_auth_find(const char *www_authenticate, /* I - Pointer into WWW-Authenticate header */
481 const char *scheme) /* I - Authentication scheme */
482{
483 size_t schemelen = strlen(scheme); /* Length of scheme */
484
485
91bc88eb
MS
486 DEBUG_printf(("8cups_auth_find(www_authenticate=\"%s\", scheme=\"%s\"(%d))", www_authenticate, scheme, (int)schemelen));
487
7ec11630
MS
488 while (*www_authenticate)
489 {
490 /*
491 * Skip leading whitespace and commas...
492 */
493
91bc88eb 494 DEBUG_printf(("9cups_auth_find: Before whitespace: \"%s\"", www_authenticate));
7ec11630
MS
495 while (isspace(*www_authenticate & 255) || *www_authenticate == ',')
496 www_authenticate ++;
91bc88eb 497 DEBUG_printf(("9cups_auth_find: After whitespace: \"%s\"", www_authenticate));
7ec11630
MS
498
499 /*
500 * See if this is "Scheme" followed by whitespace or the end of the string.
501 */
502
c944b62b 503 if (!strncmp(www_authenticate, scheme, schemelen) && (isspace(www_authenticate[schemelen] & 255) || www_authenticate[schemelen] == ',' || !www_authenticate[schemelen]))
7ec11630
MS
504 {
505 /*
506 * Yes, this is the start of the scheme-specific information...
507 */
508
91bc88eb
MS
509 DEBUG_printf(("9cups_auth_find: Returning \"%s\".", www_authenticate));
510
7ec11630
MS
511 return (www_authenticate);
512 }
513
514 /*
515 * Skip the scheme name or param="value" string...
516 */
517
518 while (!isspace(*www_authenticate & 255) && *www_authenticate)
519 {
520 if (*www_authenticate == '\"')
521 {
522 /*
523 * Skip quoted value...
524 */
525
91bc88eb
MS
526 www_authenticate ++;
527 while (*www_authenticate && *www_authenticate != '\"')
7ec11630 528 www_authenticate ++;
91bc88eb
MS
529
530 DEBUG_printf(("9cups_auth_find: After quoted: \"%s\"", www_authenticate));
7ec11630
MS
531 }
532
533 www_authenticate ++;
534 }
91bc88eb
MS
535
536 DEBUG_printf(("9cups_auth_find: After skip: \"%s\"", www_authenticate));
7ec11630
MS
537 }
538
91bc88eb
MS
539 DEBUG_puts("9cups_auth_find: Returning NULL.");
540
7ec11630
MS
541 return (NULL);
542}
543
544
545/*
546 * 'cups_auth_param()' - Copy the value for the named authentication parameter,
547 * if present.
548 */
549
550static const char * /* O - Parameter value or @code NULL@ if not present */
551cups_auth_param(const char *scheme, /* I - Pointer to auth data */
552 const char *name, /* I - Name of parameter */
553 char *value, /* I - Value buffer */
554 size_t valsize) /* I - Size of value buffer */
555{
556 char *valptr = value, /* Pointer into value buffer */
557 *valend = value + valsize - 1; /* Pointer to end of buffer */
558 size_t namelen = strlen(name); /* Name length */
559 int param; /* Is this a parameter? */
560
561
91bc88eb
MS
562 DEBUG_printf(("8cups_auth_param(scheme=\"%s\", name=\"%s\", value=%p, valsize=%d)", scheme, name, (void *)value, (int)valsize));
563
7ec11630
MS
564 while (!isspace(*scheme & 255) && *scheme)
565 scheme ++;
566
567 while (*scheme)
568 {
569 while (isspace(*scheme & 255) || *scheme == ',')
570 scheme ++;
571
572 if (!strncmp(scheme, name, namelen) && scheme[namelen] == '=')
573 {
574 /*
575 * Found the parameter, copy the value...
576 */
577
578 scheme += namelen + 1;
579 if (*scheme == '\"')
580 {
581 scheme ++;
582
583 while (*scheme && *scheme != '\"')
584 {
585 if (valptr < valend)
586 *valptr++ = *scheme;
587
588 scheme ++;
589 }
590 }
591 else
592 {
593 while (*scheme && strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~+/=", *scheme))
594 {
595 if (valptr < valend)
596 *valptr++ = *scheme;
597
598 scheme ++;
599 }
600 }
601
602 *valptr = '\0';
603
91bc88eb
MS
604 DEBUG_printf(("9cups_auth_param: Returning \"%s\".", value));
605
7ec11630
MS
606 return (value);
607 }
608
609 /*
610 * Skip the param=value string...
611 */
612
613 param = 0;
614
615 while (!isspace(*scheme & 255) && *scheme)
616 {
617 if (*scheme == '=')
618 param = 1;
619 else if (*scheme == '\"')
620 {
621 /*
622 * Skip quoted value...
623 */
624
91bc88eb
MS
625 scheme ++;
626 while (*scheme && *scheme != '\"')
7ec11630
MS
627 scheme ++;
628 }
629
630 scheme ++;
631 }
632
633 /*
634 * If this wasn't a parameter, we are at the end of this scheme's
635 * parameters...
636 */
637
638 if (!param)
639 break;
640 }
641
642 *value = '\0';
643
91bc88eb
MS
644 DEBUG_puts("9cups_auth_param: Returning NULL.");
645
7ec11630
MS
646 return (NULL);
647}
648
649
650/*
651 * 'cups_auth_scheme()' - Get the (next) WWW-Authenticate scheme.
652 *
653 * The "www_authenticate" parameter points to the current position in the header.
654 *
655 * Returns @code NULL@ if there are no (more) auth schemes present.
656 */
657
658static const char * /* O - Start of scheme or @code NULL@ if not found */
659cups_auth_scheme(const char *www_authenticate, /* I - Pointer into WWW-Authenticate header */
660 char *scheme, /* I - Scheme name buffer */
661 size_t schemesize) /* I - Size of buffer */
662{
663 const char *start; /* Start of scheme data */
664 char *sptr = scheme, /* Pointer into scheme buffer */
665 *send = scheme + schemesize - 1;/* End of scheme buffer */
666 int param; /* Is this a parameter? */
667
668
91bc88eb
MS
669 DEBUG_printf(("8cups_auth_scheme(www_authenticate=\"%s\", scheme=%p, schemesize=%d)", www_authenticate, (void *)scheme, (int)schemesize));
670
7ec11630
MS
671 while (*www_authenticate)
672 {
673 /*
674 * Skip leading whitespace and commas...
675 */
676
677 while (isspace(*www_authenticate & 255) || *www_authenticate == ',')
678 www_authenticate ++;
679
680 /*
681 * Parse the scheme name or param="value" string...
682 */
683
44cb0dd2 684 for (sptr = scheme, start = www_authenticate, param = 0; *www_authenticate && *www_authenticate != ',' && !isspace(*www_authenticate & 255); www_authenticate ++)
7ec11630
MS
685 {
686 if (*www_authenticate == '=')
687 param = 1;
688 else if (!param && sptr < send)
689 *sptr++ = *www_authenticate;
690 else if (*www_authenticate == '\"')
691 {
692 /*
693 * Skip quoted value...
694 */
695
91bc88eb
MS
696 www_authenticate ++;
697 while (*www_authenticate && *www_authenticate != '\"')
7ec11630
MS
698 www_authenticate ++;
699 }
700 }
701
702 if (sptr > scheme && !param)
703 {
704 *sptr = '\0';
91bc88eb
MS
705
706 DEBUG_printf(("9cups_auth_scheme: Returning \"%s\".", start));
707
7ec11630
MS
708 return (start);
709 }
710 }
711
712 *scheme = '\0';
713
91bc88eb
MS
714 DEBUG_puts("9cups_auth_scheme: Returning NULL.");
715
7ec11630
MS
716 return (NULL);
717}
718
719
accedcf8 720#ifdef HAVE_GSSAPI
eac3a0a0 721# ifdef HAVE_GSS_ACQUIRE_CRED_EX_F
f7deaa1a 722/*
eac3a0a0
MS
723 * 'cups_gss_acquire()' - Kerberos credentials callback.
724 */
725static void
726cups_gss_acquire(
727 void *ctx, /* I - Caller context */
728 OM_uint32 major, /* I - Major error code */
729 gss_status_id_t status, /* I - Status (unused) */
730 gss_cred_id_t creds, /* I - Credentials (if any) */
731 gss_OID_set oids, /* I - Mechanism OIDs (unused) */
732 OM_uint32 time_rec) /* I - Timestamp (unused) */
733{
734 uint32_t min; /* Minor error code */
735 _cups_gss_acquire_t *data; /* Callback data */
736
737
738 (void)status;
739 (void)time_rec;
740
741 data = (_cups_gss_acquire_t *)ctx;
742 data->major = major;
743 data->creds = creds;
744
745 gss_release_oid_set(&min, &oids);
746 dispatch_semaphore_signal(data->sem);
747}
748# endif /* HAVE_GSS_ACQUIRE_CRED_EX_F */
749
750
751/*
752 * 'cups_gss_getname()' - Get CUPS service credentials for authentication.
f7deaa1a 753 */
754
755static gss_name_t /* O - Server name */
eac3a0a0 756cups_gss_getname(
f7deaa1a 757 http_t *http, /* I - Connection to server */
758 const char *service_name) /* I - Service name */
759{
760 gss_buffer_desc token = GSS_C_EMPTY_BUFFER;
761 /* Service token */
bc44d920 762 OM_uint32 major_status, /* Major status code */
763 minor_status; /* Minor status code */
764 gss_name_t server_name; /* Server name */
eac3a0a0 765 char buf[1024]; /* Name buffer */
bc44d920 766
767
eac3a0a0 768 DEBUG_printf(("7cups_gss_getname(http=%p, service_name=\"%s\")", http,
e07d4801
MS
769 service_name));
770
771
bc44d920 772 /*
773 * Get the hostname...
774 */
f7deaa1a 775
eac3a0a0
MS
776 if (!http->gsshost[0])
777 {
778 httpGetHostname(http, http->gsshost, sizeof(http->gsshost));
bc44d920 779
eac3a0a0
MS
780 if (!strcmp(http->gsshost, "localhost"))
781 {
782 if (gethostname(http->gsshost, sizeof(http->gsshost)) < 0)
783 {
784 DEBUG_printf(("1cups_gss_getname: gethostname() failed: %s",
785 strerror(errno)));
786 http->gsshost[0] = '\0';
787 return (NULL);
788 }
789
790 if (!strchr(http->gsshost, '.'))
791 {
792 /*
793 * The hostname is not a FQDN, so look it up...
794 */
795
796 struct hostent *host; /* Host entry to get FQDN */
797
798 if ((host = gethostbyname(http->gsshost)) != NULL && host->h_name)
799 {
800 /*
801 * Use the resolved hostname...
802 */
803
804 strlcpy(http->gsshost, host->h_name, sizeof(http->gsshost));
805 }
806 else
807 {
808 DEBUG_printf(("1cups_gss_getname: gethostbyname(\"%s\") failed.",
809 http->gsshost));
810 http->gsshost[0] = '\0';
811 return (NULL);
812 }
813 }
814 }
815 }
f7deaa1a 816
817 /*
eac3a0a0 818 * Get a service name we can use for authentication purposes...
f7deaa1a 819 */
820
eac3a0a0 821 snprintf(buf, sizeof(buf), "%s@%s", service_name, http->gsshost);
f7deaa1a 822
eac3a0a0 823 DEBUG_printf(("8cups_gss_getname: Looking up \"%s\".", buf));
7ff4fea9 824
f7deaa1a 825 token.value = buf;
826 token.length = strlen(buf);
827 server_name = GSS_C_NO_NAME;
828 major_status = gss_import_name(&minor_status, &token,
829 GSS_C_NT_HOSTBASED_SERVICE,
830 &server_name);
831
832 if (GSS_ERROR(major_status))
833 {
e07d4801 834 cups_gss_printf(major_status, minor_status,
eac3a0a0 835 "cups_gss_getname: gss_import_name() failed");
f7deaa1a 836 return (NULL);
837 }
838
839 return (server_name);
840}
e07d4801
MS
841
842
843# ifdef DEBUG
844/*
eac3a0a0 845 * 'cups_gss_printf()' - Show debug error messages from GSSAPI.
e07d4801
MS
846 */
847
848static void
849cups_gss_printf(OM_uint32 major_status,/* I - Major status code */
850 OM_uint32 minor_status,/* I - Minor status code */
851 const char *message) /* I - Prefix for error message */
852{
853 OM_uint32 err_major_status, /* Major status code for display */
854 err_minor_status; /* Minor status code for display */
855 OM_uint32 msg_ctx; /* Message context */
856 gss_buffer_desc major_status_string = GSS_C_EMPTY_BUFFER,
857 /* Major status message */
858 minor_status_string = GSS_C_EMPTY_BUFFER;
859 /* Minor status message */
860
861
862 msg_ctx = 0;
863 err_major_status = gss_display_status(&err_minor_status,
864 major_status,
865 GSS_C_GSS_CODE,
866 GSS_C_NO_OID,
867 &msg_ctx,
868 &major_status_string);
869
870 if (!GSS_ERROR(err_major_status))
871 gss_display_status(&err_minor_status, minor_status, GSS_C_MECH_CODE,
872 GSS_C_NULL_OID, &msg_ctx, &minor_status_string);
873
68b10830 874 DEBUG_printf(("1%s: %s, %s", message, (char *)major_status_string.value,
e07d4801
MS
875 (char *)minor_status_string.value));
876
877 gss_release_buffer(&err_minor_status, &major_status_string);
878 gss_release_buffer(&err_minor_status, &minor_status_string);
879}
880# endif /* DEBUG */
f7deaa1a 881#endif /* HAVE_GSSAPI */
882
883
ef416fc2 884/*
885 * 'cups_local_auth()' - Get the local authorization certificate if
eac3a0a0 886 * available/applicable.
ef416fc2 887 */
888
f7deaa1a 889static int /* O - 0 if available */
bc44d920 890 /* 1 if not available */
f7deaa1a 891 /* -1 error */
ef416fc2 892cups_local_auth(http_t *http) /* I - HTTP connection to server */
893{
19dc16f7 894#if defined(_WIN32) || defined(__EMX__)
ef416fc2 895 /*
19dc16f7 896 * Currently _WIN32 and OS-2 do not support the CUPS server...
ef416fc2 897 */
898
bc44d920 899 return (1);
ef416fc2 900#else
f7deaa1a 901 int pid; /* Current process ID */
902 FILE *fp; /* Certificate file */
e07d4801 903 char trc[16], /* Try Root Certificate parameter */
6961465f 904 filename[1024]; /* Certificate filename */
7ec11630
MS
905 const char *www_auth, /* WWW-Authenticate header */
906 *schemedata; /* Data for the named auth scheme */
ef416fc2 907 _cups_globals_t *cg = _cupsGlobals(); /* Global data */
bc44d920 908# if defined(HAVE_AUTHORIZATION_H)
f7deaa1a 909 OSStatus status; /* Status */
910 AuthorizationItem auth_right; /* Authorization right */
911 AuthorizationRights auth_rights; /* Authorization rights */
912 AuthorizationFlags auth_flags; /* Authorization flags */
913 AuthorizationExternalForm auth_extrn; /* Authorization ref external */
914 char auth_key[1024]; /* Buffer */
915 char buffer[1024]; /* Buffer */
bc44d920 916# endif /* HAVE_AUTHORIZATION_H */
ef416fc2 917
918
807315e6 919 DEBUG_printf(("7cups_local_auth(http=%p) hostaddr=%s, hostname=\"%s\"", (void *)http, httpAddrString(http->hostaddr, filename, sizeof(filename)), http->hostname));
ef416fc2 920
921 /*
922 * See if we are accessing localhost...
923 */
924
7ec11630 925 if (!httpAddrLocalhost(http->hostaddr) && _cups_strcasecmp(http->hostname, "localhost") != 0)
ef416fc2 926 {
e07d4801 927 DEBUG_puts("8cups_local_auth: Not a local connection!");
bc44d920 928 return (1);
ef416fc2 929 }
930
accedcf8
MS
931 www_auth = httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE);
932
bc44d920 933# if defined(HAVE_AUTHORIZATION_H)
f7deaa1a 934 /*
935 * Delete any previous authorization reference...
936 */
f14324a7 937
b94498cf 938 if (http->auth_ref)
f7deaa1a 939 {
b94498cf 940 AuthorizationFree(http->auth_ref, kAuthorizationFlagDefaults);
941 http->auth_ref = NULL;
f7deaa1a 942 }
943
91bc88eb 944 if (!getenv("GATEWAY_INTERFACE") && (schemedata = cups_auth_find(www_auth, "AuthRef")) != NULL && cups_auth_param(schemedata, "key", auth_key, sizeof(auth_key)))
f7deaa1a 945 {
7ec11630 946 status = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &http->auth_ref);
f7deaa1a 947 if (status != errAuthorizationSuccess)
948 {
fa26ab95
MS
949 DEBUG_printf(("8cups_local_auth: AuthorizationCreate() returned %d",
950 (int)status));
f7deaa1a 951 return (-1);
952 }
953
954 auth_right.name = auth_key;
955 auth_right.valueLength = 0;
956 auth_right.value = NULL;
957 auth_right.flags = 0;
958
959 auth_rights.count = 1;
960 auth_rights.items = &auth_right;
961
f14324a7 962 auth_flags = kAuthorizationFlagDefaults |
f7deaa1a 963 kAuthorizationFlagPreAuthorize |
f14324a7 964 kAuthorizationFlagInteractionAllowed |
f7deaa1a 965 kAuthorizationFlagExtendRights;
966
f14324a7
MS
967 status = AuthorizationCopyRights(http->auth_ref, &auth_rights,
968 kAuthorizationEmptyEnvironment,
f7deaa1a 969 auth_flags, NULL);
970 if (status == errAuthorizationSuccess)
b94498cf 971 status = AuthorizationMakeExternalForm(http->auth_ref, &auth_extrn);
f7deaa1a 972
973 if (status == errAuthorizationSuccess)
974 {
975 /*
976 * Set the authorization string and return...
977 */
978
f14324a7 979 httpEncode64_2(buffer, sizeof(buffer), (void *)&auth_extrn,
f7deaa1a 980 sizeof(auth_extrn));
981
355e94dc 982 httpSetAuthString(http, "AuthRef", buffer);
f7deaa1a 983
e07d4801 984 DEBUG_printf(("8cups_local_auth: Returning authstring=\"%s\"",
f7deaa1a 985 http->authstring));
986 return (0);
987 }
988 else if (status == errAuthorizationCanceled)
989 return (-1);
990
fa26ab95 991 DEBUG_printf(("9cups_local_auth: AuthorizationCopyRights() returned %d", (int)status));
f7deaa1a 992
993 /*
994 * Fall through to try certificates...
995 */
996 }
bc44d920 997# endif /* HAVE_AUTHORIZATION_H */
f7deaa1a 998
7ec11630
MS
999# ifdef HAVE_GSSAPI
1000 if (cups_auth_find(www_auth, "Negotiate"))
1001 return (1);
1002# endif /* HAVE_GSSAPI */
7ec11630 1003
f11a948a
MS
1004# if defined(SO_PEERCRED) && defined(AF_LOCAL)
1005 /*
1006 * See if we can authenticate using the peer credentials provided over a
1007 * domain socket; if so, specify "PeerCred username" as the authentication
1008 * information...
1009 */
1010
7ec11630
MS
1011 if (http->hostaddr->addr.sa_family == AF_LOCAL &&
1012 !getenv("GATEWAY_INTERFACE") && /* Not via CGI programs... */
1013 cups_auth_find(www_auth, "PeerCred"))
f11a948a
MS
1014 {
1015 /*
1016 * Verify that the current cupsUser() matches the current UID...
1017 */
1018
1019 struct passwd *pwd; /* Password information */
1020 const char *username; /* Current username */
1021
1022 username = cupsUser();
1023
1024 if ((pwd = getpwnam(username)) != NULL && pwd->pw_uid == getuid())
1025 {
1026 httpSetAuthString(http, "PeerCred", username);
1027
1028 DEBUG_printf(("8cups_local_auth: Returning authstring=\"%s\"",
1029 http->authstring));
1030
1031 return (0);
1032 }
1033 }
1034# endif /* SO_PEERCRED && AF_LOCAL */
1035
7ec11630
MS
1036 if ((schemedata = cups_auth_find(www_auth, "Local")) == NULL)
1037 return (1);
1038
ef416fc2 1039 /*
1040 * Try opening a certificate file for this PID. If that fails,
1041 * try the root certificate...
1042 */
1043
1044 pid = getpid();
1045 snprintf(filename, sizeof(filename), "%s/certs/%d", cg->cups_statedir, pid);
1046 if ((fp = fopen(filename, "r")) == NULL && pid > 0)
1047 {
bc44d920 1048 /*
e07d4801 1049 * No certificate for this PID; see if we can get the root certificate...
bc44d920 1050 */
1051
bf265b3a 1052 DEBUG_printf(("9cups_local_auth: Unable to open file \"%s\": %s", filename, strerror(errno)));
e07d4801 1053
7ec11630 1054 if (!cups_auth_param(schemedata, "trc", trc, sizeof(trc)))
e07d4801
MS
1055 {
1056 /*
1057 * Scheduler doesn't want us to use the root certificate...
1058 */
1059
1060 return (1);
1061 }
1062
ef416fc2 1063 snprintf(filename, sizeof(filename), "%s/certs/0", cg->cups_statedir);
bf265b3a
MS
1064 if ((fp = fopen(filename, "r")) == NULL)
1065 DEBUG_printf(("9cups_local_auth: Unable to open file \"%s\": %s", filename, strerror(errno)));
ef416fc2 1066 }
1067
f7deaa1a 1068 if (fp)
ef416fc2 1069 {
f7deaa1a 1070 /*
1071 * Read the certificate from the file...
1072 */
ef416fc2 1073
6961465f
MS
1074 char certificate[33], /* Certificate string */
1075 *certptr; /* Pointer to certificate string */
1076
1077 certptr = fgets(certificate, sizeof(certificate), fp);
f7deaa1a 1078 fclose(fp);
ef416fc2 1079
6961465f
MS
1080 if (certptr)
1081 {
1082 /*
1083 * Set the authorization string and return...
1084 */
ef416fc2 1085
6961465f 1086 httpSetAuthString(http, "Local", certificate);
ef416fc2 1087
6961465f
MS
1088 DEBUG_printf(("8cups_local_auth: Returning authstring=\"%s\"",
1089 http->authstring));
ef416fc2 1090
6961465f
MS
1091 return (0);
1092 }
f7deaa1a 1093 }
1094
1095 return (1);
19dc16f7 1096#endif /* _WIN32 || __EMX__ */
ef416fc2 1097}