Coverage Report

Created: 2025-10-08 06:06

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