]> git.ipfire.org Git - thirdparty/cups.git/blame - scheduler/auth.c
Sync up with CUPS 1.6svn-r10269 (changes from Zin TOT merged into cups.org TOT)
[thirdparty/cups.git] / scheduler / auth.c
CommitLineData
b423cd4c 1/*
b19ccc9e 2 * "$Id: auth.c 7830 2008-08-04 20:38:50Z mike $"
ef416fc2 3 *
10d09e33 4 * Authorization routines for the CUPS scheduler.
ef416fc2 5 *
c8fef167 6 * Copyright 2007-2011 by Apple Inc.
f7deaa1a 7 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
8 *
9 * This file contains Kerberos support code, copyright 2006 by
10 * Jelmer Vernooij.
ef416fc2 11 *
12 * These coded instructions, statements, and computer programs are the
bc44d920 13 * property of Apple Inc. and are protected by Federal copyright
14 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
15 * which should have been included with this file. If this file is
16 * file is missing or damaged, see the license at "http://www.cups.org/".
ef416fc2 17 *
18 * Contents:
19 *
10d09e33 20 * cupsdAddIPMask() - Add an IP address authorization mask.
ef416fc2 21 * cupsdAddLocation() - Add a location for authorization.
22 * cupsdAddName() - Add a name to a location...
10d09e33
MS
23 * cupsdAddNameMask() - Add a host or interface name authorization
24 * mask.
89d46774 25 * cupsdAuthorize() - Validate any authorization credentials.
080811b1
MS
26 * cupsdCheckAccess() - Check whether the given address is allowed to
27 * access a location.
ef416fc2 28 * cupsdCheckAuth() - Check authorization masks.
29 * cupsdCheckGroup() - Check for a user's group membership.
30 * cupsdCopyLocation() - Make a copy of a location...
89d46774 31 * cupsdDeleteAllLocations() - Free all memory used for location
32 * authorization.
ef416fc2 33 * cupsdFindBest() - Find the location entry that best matches the
34 * resource.
35 * cupsdFindLocation() - Find the named location.
10d09e33 36 * cupsdFreeLocation() - Free all memory used by a location.
ef416fc2 37 * cupsdIsAuthorized() - Check to see if the user is authorized...
10d09e33 38 * cupsdNewLocation() - Create a new location for authorization.
080811b1
MS
39 * check_authref() - Check if an authorization services reference
40 * has the supplied right.
bd7854cb 41 * compare_locations() - Compare two locations.
10d09e33 42 * copy_authmask() - Copy function for auth masks.
ef416fc2 43 * cups_crypt() - Encrypt the password using the DES or MD5
44 * algorithms, as needed.
10d09e33 45 * free_authmask() - Free function for auth masks.
e1d6a774 46 * get_md5_password() - Get an MD5 password.
ef416fc2 47 * pam_func() - PAM conversation function.
48 * to64() - Base64-encode an integer value...
49 */
50
51/*
52 * Include necessary headers...
53 */
54
55#include "cupsd.h"
56#include <grp.h>
57#ifdef HAVE_SHADOW_H
58# include <shadow.h>
59#endif /* HAVE_SHADOW_H */
60#ifdef HAVE_CRYPT_H
61# include <crypt.h>
62#endif /* HAVE_CRYPT_H */
63#if HAVE_LIBPAM
64# ifdef HAVE_PAM_PAM_APPL_H
65# include <pam/pam_appl.h>
66# else
67# include <security/pam_appl.h>
68# endif /* HAVE_PAM_PAM_APPL_H */
69#endif /* HAVE_LIBPAM */
70#ifdef HAVE_USERSEC_H
71# include <usersec.h>
72#endif /* HAVE_USERSEC_H */
bd7854cb 73#ifdef HAVE_MEMBERSHIP_H
74# include <membership.h>
75#endif /* HAVE_MEMBERSHIP_H */
f7deaa1a 76#ifdef HAVE_AUTHORIZATION_H
77# include <Security/AuthorizationTags.h>
78# ifdef HAVE_SECBASEPRIV_H
79# include <Security/SecBasePriv.h>
80# else
81extern const char *cssmErrorString(int error);
82# endif /* HAVE_SECBASEPRIV_H */
83#endif /* HAVE_AUTHORIZATION_H */
db1f069b
MS
84#ifdef HAVE_SYS_PARAM_H
85# include <sys/param.h>
86#endif /* HAVE_SYS_PARAM_H */
bc44d920 87#ifdef HAVE_SYS_UCRED_H
88# include <sys/ucred.h>
89typedef struct xucred cupsd_ucred_t;
90# define CUPSD_UCRED_UID(c) (c).cr_uid
91#else
92typedef struct ucred cupsd_ucred_t;
93# define CUPSD_UCRED_UID(c) (c).uid
94#endif /* HAVE_SYS_UCRED_H */
e07d4801
MS
95#ifdef HAVE_KRB5_IPC_CLIENT_SET_TARGET_UID
96/* Not in public headers... */
97extern void krb5_ipc_client_set_target_uid(uid_t);
98extern void krb5_ipc_client_clear_target(void);
99#endif /* HAVE_KRB5_IPC_CLIENT_SET_TARGET_UID */
ef416fc2 100
101
102/*
103 * Local functions...
104 */
105
f7deaa1a 106#ifdef HAVE_AUTHORIZATION_H
107static int check_authref(cupsd_client_t *con, const char *right);
108#endif /* HAVE_AUTHORIZATION_H */
bd7854cb 109static int compare_locations(cupsd_location_t *a,
110 cupsd_location_t *b);
10d09e33 111static cupsd_authmask_t *copy_authmask(cupsd_authmask_t *am, void *data);
d09495fa 112#if !HAVE_LIBPAM && !defined(HAVE_USERSEC_H)
ef416fc2 113static char *cups_crypt(const char *pw, const char *salt);
d09495fa 114#endif /* !HAVE_LIBPAM && !HAVE_USERSEC_H */
10d09e33 115static void free_authmask(cupsd_authmask_t *am, void *data);
e1d6a774 116static char *get_md5_password(const char *username,
117 const char *group, char passwd[33]);
ef416fc2 118#if HAVE_LIBPAM
119static int pam_func(int, const struct pam_message **,
120 struct pam_response **, void *);
f7deaa1a 121#elif !defined(HAVE_USERSEC_H)
ef416fc2 122static void to64(char *s, unsigned long v, int n);
123#endif /* HAVE_LIBPAM */
124
125
126/*
127 * Local structures...
128 */
129
130#if HAVE_LIBPAM
131typedef struct cupsd_authdata_s /**** Authentication data ****/
132{
133 char username[33], /* Username string */
134 password[33]; /* Password string */
135} cupsd_authdata_t;
136#endif /* HAVE_LIBPAM */
137
138
139/*
140 * Local globals...
141 */
142
f301802f 143#if defined(__hpux) && HAVE_LIBPAM
b423cd4c 144static cupsd_authdata_t *auth_data; /* Current client being authenticated */
ef416fc2 145#endif /* __hpux && HAVE_LIBPAM */
146
147
148/*
10d09e33 149 * 'cupsdAddIPMask()' - Add an IP address authorization mask.
ef416fc2 150 */
151
10d09e33
MS
152int /* O - 1 on success, 0 on failure */
153cupsdAddIPMask(
154 cups_array_t **masks, /* IO - Masks array (created as needed) */
155 const unsigned address[4], /* I - IP address */
156 const unsigned netmask[4]) /* I - IP netmask */
ef416fc2 157{
10d09e33 158 cupsd_authmask_t temp; /* New host/domain mask */
ef416fc2 159
160
10d09e33
MS
161 cupsdLogMessage(CUPSD_LOG_DEBUG2,
162 "cupsdAddIPMask(masks=%p(%p), address=%x:%x:%x:%x, "
163 "netmask=%x:%x:%x:%x)",
164 masks, *masks,
165 address[0], address[1], address[2], address[3],
166 netmask[0], netmask[1], netmask[2], netmask[3]);
ef416fc2 167
10d09e33
MS
168 temp.type = CUPSD_AUTH_IP;
169 memcpy(temp.mask.ip.address, address, sizeof(temp.mask.ip.address));
170 memcpy(temp.mask.ip.netmask, netmask, sizeof(temp.mask.ip.netmask));
ef416fc2 171
bd7854cb 172 /*
10d09e33 173 * Create the masks array as needed and add...
bd7854cb 174 */
175
10d09e33
MS
176 if (!*masks)
177 *masks = cupsArrayNew3(NULL, NULL, NULL, 0,
178 (cups_acopy_func_t)copy_authmask,
179 (cups_afree_func_t)free_authmask);
ef416fc2 180
10d09e33
MS
181 return (cupsArrayAdd(*masks, &temp));
182}
ef416fc2 183
bd7854cb 184
10d09e33
MS
185/*
186 * 'cupsdAddLocation()' - Add a location for authorization.
187 */
ef416fc2 188
10d09e33
MS
189void
190cupsdAddLocation(cupsd_location_t *loc) /* I - Location to add */
191{
ef416fc2 192 /*
10d09e33 193 * Make sure the locations array is created...
ef416fc2 194 */
195
10d09e33
MS
196 if (!Locations)
197 Locations = cupsArrayNew3((cups_array_func_t)compare_locations, NULL,
198 (cups_ahash_func_t)NULL, 0,
199 (cups_acopy_func_t)NULL,
200 (cups_afree_func_t)cupsdFreeLocation);
201
202 if (Locations)
203 {
204 cupsArrayAdd(Locations, loc);
205
206 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdAddLocation: Added location \"%s\"",
207 loc->location ? loc->location : "(null)");
208 }
ef416fc2 209}
210
211
212/*
213 * 'cupsdAddName()' - Add a name to a location...
214 */
215
216void
217cupsdAddName(cupsd_location_t *loc, /* I - Location to add to */
218 char *name) /* I - Name to add */
219{
ef416fc2 220 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdAddName(loc=%p, name=\"%s\")",
221 loc, name);
222
10d09e33
MS
223 if (!loc->names)
224 loc->names = cupsArrayNew3(NULL, NULL, NULL, 0,
225 (cups_acopy_func_t)_cupsStrAlloc,
226 (cups_afree_func_t)_cupsStrFree);
ef416fc2 227
10d09e33 228 if (!cupsArrayAdd(loc->names, name))
ef416fc2 229 {
230 cupsdLogMessage(CUPSD_LOG_ERROR,
231 "Unable to duplicate name for location %s: %s",
e1d6a774 232 loc->location ? loc->location : "nil", strerror(errno));
ef416fc2 233 return;
234 }
ef416fc2 235}
236
237
238/*
10d09e33 239 * 'cupsdAddNameMask()' - Add a host or interface name authorization mask.
ef416fc2 240 */
241
10d09e33
MS
242int /* O - 1 on success, 0 on failure */
243cupsdAddNameMask(cups_array_t **masks, /* IO - Masks array (created as needed) */
244 char *name) /* I - Host or interface name */
ef416fc2 245{
10d09e33 246 cupsd_authmask_t temp; /* New host/domain mask */
ef416fc2 247 char ifname[32], /* Interface name */
248 *ifptr; /* Pointer to end of name */
249
250
10d09e33
MS
251 cupsdLogMessage(CUPSD_LOG_DEBUG2,
252 "cupsdAddNameMask(masks=%p(%p), name=\"%s\")",
253 masks, *masks, name);
ef416fc2 254
88f9aafc 255 if (!_cups_strcasecmp(name, "@LOCAL"))
ef416fc2 256 {
257 /*
10d09e33 258 * Deny *interface*...
ef416fc2 259 */
260
10d09e33
MS
261 temp.type = CUPSD_AUTH_INTERFACE;
262 temp.mask.name.name = (char *)"*";
ef416fc2 263 }
88f9aafc 264 else if (!_cups_strncasecmp(name, "@IF(", 4))
ef416fc2 265 {
266 /*
10d09e33 267 * Deny *interface*...
ef416fc2 268 */
269
270 strlcpy(ifname, name + 4, sizeof(ifname));
271
10d09e33 272 ifptr = ifname + strlen(ifname) - 1;
ef416fc2 273
10d09e33 274 if (ifptr >= ifname && *ifptr == ')')
ef416fc2 275 {
276 ifptr --;
277 *ifptr = '\0';
278 }
279
10d09e33
MS
280 temp.type = CUPSD_AUTH_INTERFACE;
281 temp.mask.name.name = ifname;
ef416fc2 282 }
283 else
284 {
285 /*
10d09e33 286 * Deny name...
ef416fc2 287 */
288
10d09e33
MS
289 if (*name == '*')
290 name ++;
ef416fc2 291
10d09e33
MS
292 temp.type = CUPSD_AUTH_NAME;
293 temp.mask.name.name = (char *)name;
294 }
ef416fc2 295
10d09e33
MS
296 /*
297 * Set the name length...
298 */
ef416fc2 299
10d09e33 300 temp.mask.name.length = strlen(temp.mask.name.name);
ef416fc2 301
10d09e33
MS
302 /*
303 * Create the masks array as needed and add...
304 */
ef416fc2 305
10d09e33
MS
306 if (!*masks)
307 *masks = cupsArrayNew3(NULL, NULL, NULL, 0,
308 (cups_acopy_func_t)copy_authmask,
309 (cups_afree_func_t)free_authmask);
ef416fc2 310
10d09e33 311 return (cupsArrayAdd(*masks, &temp));
ef416fc2 312}
313
314
315/*
316 * 'cupsdAuthorize()' - Validate any authorization credentials.
317 */
318
319void
320cupsdAuthorize(cupsd_client_t *con) /* I - Client connection */
321{
322 int type; /* Authentication type */
f7deaa1a 323 const char *authorization; /* Pointer into Authorization string */
324 char *ptr, /* Pointer into string */
db1f069b 325 username[256], /* Username string */
ef416fc2 326 password[33]; /* Password string */
db0bd74a 327 cupsd_cert_t *localuser; /* Certificate username */
ef416fc2 328 char nonce[HTTP_MAX_VALUE], /* Nonce value from client */
329 md5[33], /* MD5 password */
330 basicmd5[33]; /* MD5 of Basic password */
331 static const char * const states[] = /* HTTP client states... */
332 {
333 "WAITING",
334 "OPTIONS",
335 "GET",
336 "GET",
337 "HEAD",
338 "POST",
339 "POST",
340 "POST",
341 "PUT",
342 "PUT",
343 "DELETE",
344 "TRACE",
345 "CLOSE",
346 "STATUS"
347 };
348
349
350 /*
351 * Locate the best matching location so we know what kind of
352 * authentication to expect...
353 */
354
355 con->best = cupsdFindBest(con->uri, con->http.state);
5bd77a73 356 con->type = CUPSD_AUTH_NONE;
ef416fc2 357
358 cupsdLogMessage(CUPSD_LOG_DEBUG2,
dcb445bc
MS
359 "[Client %d] con->uri=\"%s\", con->best=%p(%s)",
360 con->http.fd, con->uri, con->best,
361 con->best ? con->best->location : "");
ef416fc2 362
5bd77a73 363 if (con->best && con->best->type != CUPSD_AUTH_NONE)
7ff4fea9 364 {
5bd77a73 365 if (con->best->type == CUPSD_AUTH_DEFAULT)
dcb445bc 366 type = cupsdDefaultAuthType();
7ff4fea9
MS
367 else
368 type = con->best->type;
369 }
ef416fc2 370 else
dcb445bc 371 type = cupsdDefaultAuthType();
ef416fc2 372
373 /*
374 * Decode the Authorization string...
375 */
376
f7deaa1a 377 authorization = httpGetField(&con->http, HTTP_FIELD_AUTHORIZATION);
ef416fc2 378
dcb445bc
MS
379 cupsdLogMessage(CUPSD_LOG_DEBUG2, "[Client %d] Authorization=\"%s\"",
380 con->http.fd, authorization);
ef416fc2 381
382 username[0] = '\0';
383 password[0] = '\0';
384
07ed0e9a
MS
385#ifdef HAVE_GSSAPI
386 con->gss_uid = 0;
387#endif /* HAVE_GSSAPI */
388
f7deaa1a 389#ifdef HAVE_AUTHORIZATION_H
390 if (con->authref)
391 {
392 AuthorizationFree(con->authref, kAuthorizationFlagDefaults);
393 con->authref = NULL;
394 }
395#endif /* HAVE_AUTHORIZATION_H */
396
2fb76298 397 if (!*authorization)
ef416fc2 398 {
399 /*
400 * No authorization data provided, return early...
401 */
402
403 cupsdLogMessage(CUPSD_LOG_DEBUG,
dcb445bc
MS
404 "[Client %d] No authentication data provided.",
405 con->http.fd);
ef416fc2 406 return;
407 }
f7deaa1a 408#ifdef HAVE_AUTHORIZATION_H
c8fef167 409 else if (!strncmp(authorization, "AuthRef ", 8) &&
88f9aafc 410 !_cups_strcasecmp(con->http.hostname, "localhost"))
f7deaa1a 411 {
412 OSStatus status; /* Status */
413 int authlen; /* Auth string length */
414 AuthorizationItemSet *authinfo; /* Authorization item set */
415
416 /*
417 * Get the Authorization Services data...
418 */
419
c8fef167 420 authorization += 8;
f7deaa1a 421 while (isspace(*authorization & 255))
422 authorization ++;
423
424 authlen = sizeof(nonce);
425 httpDecode64_2(nonce, &authlen, authorization);
426
427 if (authlen != kAuthorizationExternalFormLength)
428 {
429 cupsdLogMessage(CUPSD_LOG_ERROR,
dcb445bc
MS
430 "[Client %d] External Authorization reference size is "
431 "incorrect.", con->http.fd);
f7deaa1a 432 return;
433 }
434
435 if ((status = AuthorizationCreateFromExternalForm(
436 (AuthorizationExternalForm *)nonce, &con->authref)) != 0)
437 {
438 cupsdLogMessage(CUPSD_LOG_ERROR,
dcb445bc
MS
439 "[Client %d] AuthorizationCreateFromExternalForm "
440 "returned %d (%s)", con->http.fd, (int)status,
441 cssmErrorString(status));
f7deaa1a 442 return;
443 }
444
c8fef167 445 username[0] = '\0';
ed6e7faf 446
c8fef167 447 if (!AuthorizationCopyInfo(con->authref, kAuthorizationEnvironmentUsername,
bf3816c7 448 &authinfo))
e4572d57 449 {
ed6e7faf
MS
450 if (authinfo->count == 1 && authinfo->items[0].value &&
451 authinfo->items[0].valueLength >= 2)
c8fef167 452 {
ed6e7faf
MS
453 strlcpy(username, authinfo->items[0].value, sizeof(username));
454
c8fef167 455 cupsdLogMessage(CUPSD_LOG_DEBUG,
dcb445bc
MS
456 "[Client %d] Authorized as \"%s\" using AuthRef",
457 con->http.fd, username);
c8fef167
MS
458 }
459
e4572d57 460 AuthorizationFreeItemSet(authinfo);
e4572d57 461 }
355e94dc 462
c8fef167
MS
463 if (!username[0])
464 {
465 /*
466 * No username in AuthRef, grab username using peer credentials...
467 */
468
469 struct passwd *pwd; /* Password entry for this user */
470 cupsd_ucred_t peercred; /* Peer credentials */
471 socklen_t peersize; /* Size of peer credentials */
472
473 peersize = sizeof(peercred);
474
475 if (getsockopt(con->http.fd, 0, LOCAL_PEERCRED, &peercred, &peersize))
476 {
dcb445bc
MS
477 cupsdLogMessage(CUPSD_LOG_ERROR,
478 "[Client %d] Unable to get peer credentials - %s",
479 con->http.fd, strerror(errno));
c8fef167
MS
480 return;
481 }
482
483 if ((pwd = getpwuid(CUPSD_UCRED_UID(peercred))) == NULL)
484 {
485 cupsdLogMessage(CUPSD_LOG_ERROR,
dcb445bc
MS
486 "[Client %d] Unable to find UID %d for peer "
487 "credentials.", con->http.fd,
c8fef167
MS
488 (int)CUPSD_UCRED_UID(peercred));
489 return;
490 }
491
492 strlcpy(username, pwd->pw_name, sizeof(username));
493
494 cupsdLogMessage(CUPSD_LOG_DEBUG,
dcb445bc
MS
495 "[Client %d] Authorized as \"%s\" using "
496 "AuthRef + PeerCred", con->http.fd, username);
c8fef167
MS
497 }
498
5bd77a73 499 con->type = CUPSD_AUTH_BASIC;
f7deaa1a 500 }
501#endif /* HAVE_AUTHORIZATION_H */
bc44d920 502#if defined(SO_PEERCRED) && defined(AF_LOCAL)
503 else if (!strncmp(authorization, "PeerCred ", 9) &&
504 con->http.hostaddr->addr.sa_family == AF_LOCAL)
505 {
506 /*
507 * Use peer credentials from domain socket connection...
508 */
509
510 struct passwd *pwd; /* Password entry for this user */
511 cupsd_ucred_t peercred; /* Peer credentials */
512 socklen_t peersize; /* Size of peer credentials */
f14324a7
MS
513#ifdef HAVE_AUTHORIZATION_H
514 const char *name; /* Authorizing name */
bc44d920 515
f14324a7
MS
516 for (name = (char *)cupsArrayFirst(con->best->names);
517 name;
518 name = (char *)cupsArrayNext(con->best->names))
dcb445bc
MS
519 if (!_cups_strncasecmp(name, "@AUTHKEY(", 9) ||
520 !_cups_strcasecmp(name, "@SYSTEM"))
f14324a7
MS
521 {
522 cupsdLogMessage(CUPSD_LOG_ERROR,
dcb445bc
MS
523 "[Client %d] PeerCred authentication not allowed for "
524 "resource.", con->http.fd);
f14324a7
MS
525 return;
526 }
527#endif /* HAVE_AUTHORIZATION_H */
bc44d920 528
529 if ((pwd = getpwnam(authorization + 9)) == NULL)
530 {
dcb445bc
MS
531 cupsdLogMessage(CUPSD_LOG_ERROR,
532 "[Client %d] User \"%s\" does not exist.", con->http.fd,
bc44d920 533 authorization + 9);
534 return;
535 }
536
537 peersize = sizeof(peercred);
538
f11a948a
MS
539# ifdef __APPLE__
540 if (getsockopt(con->http.fd, 0, LOCAL_PEERCRED, &peercred, &peersize))
541# else
bc44d920 542 if (getsockopt(con->http.fd, SOL_SOCKET, SO_PEERCRED, &peercred, &peersize))
f11a948a 543# endif /* __APPLE__ */
bc44d920 544 {
dcb445bc
MS
545 cupsdLogMessage(CUPSD_LOG_ERROR,
546 "[Client %d] Unable to get peer credentials - %s",
547 con->http.fd, strerror(errno));
bc44d920 548 return;
549 }
550
551 if (pwd->pw_uid != CUPSD_UCRED_UID(peercred))
552 {
553 cupsdLogMessage(CUPSD_LOG_ERROR,
dcb445bc
MS
554 "[Client %d] Invalid peer credentials for \"%s\" - got "
555 "%d, expected %d!", con->http.fd, authorization + 9,
bc44d920 556 CUPSD_UCRED_UID(peercred), pwd->pw_uid);
557# ifdef HAVE_SYS_UCRED_H
dcb445bc
MS
558 cupsdLogMessage(CUPSD_LOG_DEBUG, "[Client %d] cr_version=%d",
559 con->http.fd, peercred.cr_version);
560 cupsdLogMessage(CUPSD_LOG_DEBUG, "[Client %d] cr_uid=%d",
561 con->http.fd, peercred.cr_uid);
562 cupsdLogMessage(CUPSD_LOG_DEBUG, "[Client %d] cr_ngroups=%d",
563 con->http.fd, peercred.cr_ngroups);
564 cupsdLogMessage(CUPSD_LOG_DEBUG, "[Client %d] cr_groups[0]=%d",
565 con->http.fd, peercred.cr_groups[0]);
bc44d920 566# endif /* HAVE_SYS_UCRED_H */
567 return;
568 }
569
570 strlcpy(username, authorization + 9, sizeof(username));
355e94dc 571
eac3a0a0
MS
572# ifdef HAVE_GSSAPI
573 con->gss_uid = CUPSD_UCRED_UID(peercred);
574# endif /* HAVE_GSSAPI */
575
355e94dc 576 cupsdLogMessage(CUPSD_LOG_DEBUG,
dcb445bc 577 "[Client %d] Authorized as %s using PeerCred", con->http.fd,
355e94dc 578 username);
2fb76298 579
5bd77a73 580 con->type = CUPSD_AUTH_BASIC;
bc44d920 581 }
582#endif /* SO_PEERCRED && AF_LOCAL */
ef416fc2 583 else if (!strncmp(authorization, "Local", 5) &&
88f9aafc 584 !_cups_strcasecmp(con->http.hostname, "localhost"))
ef416fc2 585 {
586 /*
587 * Get Local certificate authentication data...
588 */
589
590 authorization += 5;
80ca4592 591 while (isspace(*authorization & 255))
ef416fc2 592 authorization ++;
593
594 if ((localuser = cupsdFindCert(authorization)) != NULL)
355e94dc 595 {
db0bd74a 596 strlcpy(username, localuser->username, sizeof(username));
355e94dc
MS
597
598 cupsdLogMessage(CUPSD_LOG_DEBUG,
dcb445bc 599 "[Client %d] Authorized as %s using Local", con->http.fd,
355e94dc
MS
600 username);
601 }
ef416fc2 602 else
603 {
604 cupsdLogMessage(CUPSD_LOG_ERROR,
dcb445bc
MS
605 "[Client %d] Local authentication certificate not found.",
606 con->http.fd);
ef416fc2 607 return;
608 }
2fb76298 609
db0bd74a
MS
610#ifdef HAVE_GSSAPI
611 if (localuser->ccache)
612 con->type = CUPSD_AUTH_NEGOTIATE;
613 else
614#endif /* HAVE_GSSAPI */
615 con->type = CUPSD_AUTH_BASIC;
ef416fc2 616 }
2fb76298 617 else if (!strncmp(authorization, "Basic", 5))
ef416fc2 618 {
619 /*
620 * Get the Basic authentication data...
621 */
622
623 int userlen; /* Username:password length */
624
625
626 authorization += 5;
80ca4592 627 while (isspace(*authorization & 255))
ef416fc2 628 authorization ++;
629
630 userlen = sizeof(username);
631 httpDecode64_2(username, &userlen, authorization);
632
633 /*
634 * Pull the username and password out...
635 */
636
637 if ((ptr = strchr(username, ':')) == NULL)
638 {
dcb445bc
MS
639 cupsdLogMessage(CUPSD_LOG_ERROR, "[Client %d] Missing Basic password.",
640 con->http.fd);
ef416fc2 641 return;
642 }
643
644 *ptr++ = '\0';
645
646 if (!username[0])
647 {
648 /*
649 * Username must not be empty...
650 */
651
dcb445bc
MS
652 cupsdLogMessage(CUPSD_LOG_ERROR, "[Client %d] Empty Basic username.",
653 con->http.fd);
ef416fc2 654 return;
655 }
656
657 if (!*ptr)
658 {
659 /*
660 * Password must not be empty...
661 */
662
dcb445bc
MS
663 cupsdLogMessage(CUPSD_LOG_ERROR, "[Client %d] Empty Basic password.",
664 con->http.fd);
ef416fc2 665 return;
666 }
667
668 strlcpy(password, ptr, sizeof(password));
669
670 /*
671 * Validate the username and password...
672 */
673
674 switch (type)
675 {
2fb76298 676 default :
5bd77a73 677 case CUPSD_AUTH_BASIC :
ef416fc2 678 {
679#if HAVE_LIBPAM
680 /*
681 * Only use PAM to do authentication. This supports MD5
682 * passwords, among other things...
683 */
684
685 pam_handle_t *pamh; /* PAM authentication handle */
686 int pamerr; /* PAM error code */
687 struct pam_conv pamdata;/* PAM conversation data */
688 cupsd_authdata_t data; /* Authentication data */
689
690
691 strlcpy(data.username, username, sizeof(data.username));
692 strlcpy(data.password, password, sizeof(data.password));
693
07725fee 694# if defined(__sun) || defined(__hpux)
e1d6a774 695 pamdata.conv = (int (*)(int, struct pam_message **,
696 struct pam_response **,
697 void *))pam_func;
698# else
ef416fc2 699 pamdata.conv = pam_func;
07725fee 700# endif /* __sun || __hpux */
ef416fc2 701 pamdata.appdata_ptr = &data;
702
703# ifdef __hpux
704 /*
705 * Workaround for HP-UX bug in pam_unix; see pam_func() below for
706 * more info...
707 */
708
709 auth_data = &data;
710# endif /* __hpux */
711
712 pamerr = pam_start("cups", username, &pamdata, &pamh);
713 if (pamerr != PAM_SUCCESS)
714 {
715 cupsdLogMessage(CUPSD_LOG_ERROR,
dcb445bc
MS
716 "[Client %d] pam_start() returned %d (%s)",
717 con->http.fd, pamerr, pam_strerror(pamh, pamerr));
ef416fc2 718 return;
719 }
720
ee571f26
MS
721# ifdef HAVE_PAM_SET_ITEM
722# ifdef PAM_RHOST
a4924f6c
MS
723 pamerr = pam_set_item(pamh, PAM_RHOST, con->http.hostname);
724 if (pamerr != PAM_SUCCESS)
725 cupsdLogMessage(CUPSD_LOG_WARN,
dcb445bc
MS
726 "[Client %d] pam_set_item(PAM_RHOST) "
727 "returned %d (%s)", con->http.fd, pamerr,
ee571f26
MS
728 pam_strerror(pamh, pamerr));
729# endif /* PAM_RHOST */
730
731# ifdef PAM_TTY
732 pamerr = pam_set_item(pamh, PAM_TTY, "cups");
733 if (pamerr != PAM_SUCCESS)
734 cupsdLogMessage(CUPSD_LOG_WARN,
dcb445bc
MS
735 "[Client %d] pam_set_item(PAM_TTY) "
736 "returned %d (%s)!", con->http.fd, pamerr,
ee571f26
MS
737 pam_strerror(pamh, pamerr));
738# endif /* PAM_TTY */
739# endif /* HAVE_PAM_SET_ITEM */
740
ef416fc2 741 pamerr = pam_authenticate(pamh, PAM_SILENT);
742 if (pamerr != PAM_SUCCESS)
743 {
744 cupsdLogMessage(CUPSD_LOG_ERROR,
dcb445bc
MS
745 "[Client %d] pam_authenticate() returned %d (%s)",
746 con->http.fd, pamerr, pam_strerror(pamh, pamerr));
ef416fc2 747 pam_end(pamh, 0);
748 return;
749 }
750
94da7e34
MS
751# ifdef HAVE_PAM_SETCRED
752 pamerr = pam_setcred(pamh, PAM_ESTABLISH_CRED | PAM_SILENT);
753 if (pamerr != PAM_SUCCESS)
754 cupsdLogMessage(CUPSD_LOG_WARN,
dcb445bc
MS
755 "[Client %d] pam_setcred() returned %d (%s)",
756 con->http.fd, pamerr,
94da7e34
MS
757 pam_strerror(pamh, pamerr));
758# endif /* HAVE_PAM_SETCRED */
759
ef416fc2 760 pamerr = pam_acct_mgmt(pamh, PAM_SILENT);
761 if (pamerr != PAM_SUCCESS)
762 {
763 cupsdLogMessage(CUPSD_LOG_ERROR,
dcb445bc
MS
764 "[Client %d] pam_acct_mgmt() returned %d (%s)",
765 con->http.fd, pamerr, pam_strerror(pamh, pamerr));
ef416fc2 766 pam_end(pamh, 0);
767 return;
768 }
769
770 pam_end(pamh, PAM_SUCCESS);
771
772#elif defined(HAVE_USERSEC_H)
773 /*
774 * Use AIX authentication interface...
775 */
776
777 char *authmsg; /* Authentication message */
ef416fc2 778 int reenter; /* ??? */
779
780
781 cupsdLogMessage(CUPSD_LOG_DEBUG,
dcb445bc
MS
782 "[Client %d] AIX authenticate of username \"%s\"",
783 con->http.fd, username);
ef416fc2 784
785 reenter = 1;
786 if (authenticate(username, password, &reenter, &authmsg) != 0)
787 {
788 cupsdLogMessage(CUPSD_LOG_DEBUG,
dcb445bc
MS
789 "[Client %d] Unable to authenticate username "
790 "\"%s\": %s", con->http.fd, username,
791 strerror(errno));
ef416fc2 792 return;
793 }
794
795#else
796 /*
797 * Use normal UNIX password file-based authentication...
798 */
799
800 char *pass; /* Encrypted password */
801 struct passwd *pw; /* User password data */
802# ifdef HAVE_SHADOW_H
803 struct spwd *spw; /* Shadow password data */
804# endif /* HAVE_SHADOW_H */
805
806
807 pw = getpwnam(username); /* Get the current password */
808 endpwent(); /* Close the password file */
809
810 if (!pw)
811 {
812 /*
813 * No such user...
814 */
815
816 cupsdLogMessage(CUPSD_LOG_ERROR,
dcb445bc
MS
817 "[Client %d] Unknown username \"%s\".",
818 con->http.fd, username);
80ca4592 819 return;
ef416fc2 820 }
821
822# ifdef HAVE_SHADOW_H
823 spw = getspnam(username);
824 endspent();
825
826 if (!spw && !strcmp(pw->pw_passwd, "x"))
827 {
828 /*
829 * Don't allow blank passwords!
830 */
831
832 cupsdLogMessage(CUPSD_LOG_ERROR,
dcb445bc
MS
833 "[Client %d] Username \"%s\" has no shadow "
834 "password.", con->http.fd, username);
ef416fc2 835 return;
836 }
837
838 if (spw && !spw->sp_pwdp[0] && !pw->pw_passwd[0])
839# else
840 if (!pw->pw_passwd[0])
841# endif /* HAVE_SHADOW_H */
842 {
843 /*
844 * Don't allow blank passwords!
845 */
846
847 cupsdLogMessage(CUPSD_LOG_ERROR,
dcb445bc 848 "Username \"%s\" has no password.", con->http.fd,
ef416fc2 849 username);
850 return;
851 }
852
853 /*
854 * OK, the password isn't blank, so compare with what came from the
855 * client...
856 */
857
858 pass = cups_crypt(password, pw->pw_passwd);
859
860 cupsdLogMessage(CUPSD_LOG_DEBUG2,
dcb445bc
MS
861 "[Client %d] pw_passwd=\"%s\", crypt=\"%s\"",
862 con->http.fd, pw->pw_passwd, pass);
ef416fc2 863
864 if (!pass || strcmp(pw->pw_passwd, pass))
865 {
866# ifdef HAVE_SHADOW_H
867 if (spw)
868 {
869 pass = cups_crypt(password, spw->sp_pwdp);
870
871 cupsdLogMessage(CUPSD_LOG_DEBUG2,
dcb445bc
MS
872 "[Client %d] sp_pwdp=\"%s\", crypt=\"%s\"",
873 con->http.fd, spw->sp_pwdp, pass);
ef416fc2 874
875 if (pass == NULL || strcmp(spw->sp_pwdp, pass))
876 {
877 cupsdLogMessage(CUPSD_LOG_ERROR,
dcb445bc
MS
878 "[Client %d] Authentication failed for user "
879 "\"%s\".", con->http.fd, username);
ef416fc2 880 return;
881 }
882 }
883 else
884# endif /* HAVE_SHADOW_H */
885 {
886 cupsdLogMessage(CUPSD_LOG_ERROR,
dcb445bc
MS
887 "[Client %d] Authentication failed for user "
888 "\"%s\".", con->http.fd, username);
ef416fc2 889 return;
890 }
891 }
892#endif /* HAVE_LIBPAM */
893 }
355e94dc
MS
894
895 cupsdLogMessage(CUPSD_LOG_DEBUG,
dcb445bc
MS
896 "[Client %d] Authorized as %s using Basic",
897 con->http.fd, username);
ef416fc2 898 break;
899
5bd77a73 900 case CUPSD_AUTH_BASICDIGEST :
ef416fc2 901 /*
902 * Do Basic authentication with the Digest password file...
903 */
904
e1d6a774 905 if (!get_md5_password(username, NULL, md5))
ef416fc2 906 {
907 cupsdLogMessage(CUPSD_LOG_ERROR,
dcb445bc
MS
908 "[Client %d] Unknown MD5 username \"%s\".",
909 con->http.fd, username);
ef416fc2 910 return;
911 }
912
913 httpMD5(username, "CUPS", password, basicmd5);
914
915 if (strcmp(md5, basicmd5))
916 {
917 cupsdLogMessage(CUPSD_LOG_ERROR,
dcb445bc
MS
918 "[Client %d] Authentication failed for \"%s\".",
919 con->http.fd, username);
ef416fc2 920 return;
921 }
355e94dc
MS
922
923 cupsdLogMessage(CUPSD_LOG_DEBUG,
dcb445bc
MS
924 "[Client %d] Authorized as %s using BasicDigest",
925 con->http.fd, username);
ef416fc2 926 break;
927 }
2fb76298
MS
928
929 con->type = type;
ef416fc2 930 }
2fb76298 931 else if (!strncmp(authorization, "Digest", 6))
ef416fc2 932 {
933 /*
934 * Get the username, password, and nonce from the Digest attributes...
935 */
936
937 if (!httpGetSubField2(&(con->http), HTTP_FIELD_AUTHORIZATION, "username",
938 username, sizeof(username)) || !username[0])
939 {
940 /*
941 * Username must not be empty...
942 */
943
944 cupsdLogMessage(CUPSD_LOG_ERROR,
dcb445bc
MS
945 "[Client %d] Empty or missing Digest username.",
946 con->http.fd);
ef416fc2 947 return;
948 }
949
950 if (!httpGetSubField2(&(con->http), HTTP_FIELD_AUTHORIZATION, "response",
951 password, sizeof(password)) || !password[0])
952 {
953 /*
954 * Password must not be empty...
955 */
956
957 cupsdLogMessage(CUPSD_LOG_ERROR,
dcb445bc
MS
958 "[Client %d] Empty or missing Digest password.",
959 con->http.fd);
ef416fc2 960 return;
961 }
962
963 if (!httpGetSubField(&(con->http), HTTP_FIELD_AUTHORIZATION, "nonce",
964 nonce))
965 {
966 cupsdLogMessage(CUPSD_LOG_ERROR,
dcb445bc
MS
967 "[Client %d] No nonce value for Digest authentication.",
968 con->http.fd);
ef416fc2 969 return;
970 }
971
972 if (strcmp(con->http.hostname, nonce))
973 {
974 cupsdLogMessage(CUPSD_LOG_ERROR,
dcb445bc
MS
975 "[Client %d] Bad nonce value, expected \"%s\", "
976 "got \"%s\".", con->http.fd, con->http.hostname, nonce);
ef416fc2 977 return;
978 }
979
980 /*
981 * Validate the username and password...
982 */
983
e1d6a774 984 if (!get_md5_password(username, NULL, md5))
ef416fc2 985 {
986 cupsdLogMessage(CUPSD_LOG_ERROR,
dcb445bc
MS
987 "[Client %d] Unknown MD5 username \"%s\".",
988 con->http.fd, username);
ef416fc2 989 return;
990 }
991
992 httpMD5Final(nonce, states[con->http.state], con->uri, md5);
993
994 if (strcmp(md5, password))
995 {
996 cupsdLogMessage(CUPSD_LOG_ERROR,
dcb445bc
MS
997 "[Client %d] Authentication failed for \"%s\".",
998 con->http.fd, username);
ef416fc2 999 return;
1000 }
355e94dc
MS
1001
1002 cupsdLogMessage(CUPSD_LOG_DEBUG,
dcb445bc 1003 "[Client %d] Authorized as %s using Digest", con->http.fd,
355e94dc 1004 username);
2fb76298 1005
5bd77a73 1006 con->type = CUPSD_AUTH_DIGEST;
ef416fc2 1007 }
f7deaa1a 1008#ifdef HAVE_GSSAPI
c8fef167 1009 else if (!strncmp(authorization, "Negotiate", 9))
f7deaa1a 1010 {
1011 int len; /* Length of authorization string */
f7deaa1a 1012 gss_ctx_id_t context; /* Authorization context */
1013 OM_uint32 major_status, /* Major status code */
1014 minor_status; /* Minor status code */
1015 gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER,
1016 /* Input token from string */
1017 output_token = GSS_C_EMPTY_BUFFER;
1018 /* Output token for username */
1019 gss_name_t client_name; /* Client name */
1020
1021
f42414bf 1022# ifdef __APPLE__
1023 /*
1024 * If the weak-linked GSSAPI/Kerberos library is not present, don't try
1025 * to use it...
1026 */
1027
1028 if (gss_init_sec_context == NULL)
1029 {
1030 cupsdLogMessage(CUPSD_LOG_WARN,
dcb445bc
MS
1031 "[Client %d] GSSAPI/Kerberos authentication failed "
1032 "because the Kerberos framework is not present.",
1033 con->http.fd);
f42414bf 1034 return;
1035 }
1036# endif /* __APPLE__ */
1037
f7deaa1a 1038 /*
1039 * Find the start of the Kerberos input token...
1040 */
1041
1042 authorization += 9;
1043 while (isspace(*authorization & 255))
1044 authorization ++;
1045
1046 if (!*authorization)
1047 {
1048 cupsdLogMessage(CUPSD_LOG_DEBUG2,
dcb445bc
MS
1049 "[Client %d] No authentication data specified.",
1050 con->http.fd);
f7deaa1a 1051 return;
1052 }
1053
f7deaa1a 1054 /*
1055 * Decode the authorization string to get the input token...
1056 */
1057
1058 len = strlen(authorization);
1059 input_token.value = malloc(len);
1060 input_token.value = httpDecode64_2(input_token.value, &len,
c7017ecc 1061 authorization);
f7deaa1a 1062 input_token.length = len;
1063
1064 /*
1065 * Accept the input token to get the authorization info...
1066 */
1067
1068 context = GSS_C_NO_CONTEXT;
1069 client_name = GSS_C_NO_NAME;
1070 major_status = gss_accept_sec_context(&minor_status,
1071 &context,
dcb445bc 1072 ServerCreds,
f7deaa1a 1073 &input_token,
1074 GSS_C_NO_CHANNEL_BINDINGS,
1075 &client_name,
1076 NULL,
07ed0e9a
MS
1077 &output_token,
1078 NULL,
f7deaa1a 1079 NULL,
07ed0e9a
MS
1080 NULL);
1081
1082 if (output_token.length > 0)
1083 gss_release_buffer(&minor_status, &output_token);
f7deaa1a 1084
1085 if (GSS_ERROR(major_status))
1086 {
1087 cupsdLogGSSMessage(CUPSD_LOG_DEBUG, major_status, minor_status,
dcb445bc
MS
1088 "[Client %d] Error accepting GSSAPI security context",
1089 con->http.fd);
f7deaa1a 1090
1091 if (context != GSS_C_NO_CONTEXT)
1092 gss_delete_sec_context(&minor_status, &context, GSS_C_NO_BUFFER);
f7deaa1a 1093 return;
1094 }
1095
97c9a8d7
MS
1096 con->have_gss = 1;
1097
76cd9e37
MS
1098 /*
1099 * Get the username associated with the client's credentials...
f7deaa1a 1100 */
1101
355e94dc
MS
1102 if (major_status == GSS_S_CONTINUE_NEEDED)
1103 cupsdLogGSSMessage(CUPSD_LOG_DEBUG, major_status, minor_status,
dcb445bc 1104 "[Client %d] Credentials not complete", con->http.fd);
355e94dc 1105 else if (major_status == GSS_S_COMPLETE)
f7deaa1a 1106 {
c8fef167 1107 major_status = gss_display_name(&minor_status, client_name,
f7deaa1a 1108 &output_token, NULL);
1109
1110 if (GSS_ERROR(major_status))
1111 {
1112 cupsdLogGSSMessage(CUPSD_LOG_DEBUG, major_status, minor_status,
dcb445bc 1113 "[Client %d] Error getting username", con->http.fd);
f7deaa1a 1114 gss_release_name(&minor_status, &client_name);
1115 gss_delete_sec_context(&minor_status, &context, GSS_C_NO_BUFFER);
f7deaa1a 1116 return;
1117 }
1118
f7deaa1a 1119 strlcpy(username, output_token.value, sizeof(username));
355e94dc
MS
1120
1121 cupsdLogMessage(CUPSD_LOG_DEBUG,
dcb445bc
MS
1122 "[Client %d] Authorized as %s using Negotiate",
1123 con->http.fd, username);
f7deaa1a 1124
e07d4801 1125 gss_release_name(&minor_status, &client_name);
f7deaa1a 1126 gss_release_buffer(&minor_status, &output_token);
2fb76298 1127
5bd77a73 1128 con->type = CUPSD_AUTH_NEGOTIATE;
f7deaa1a 1129 }
e07d4801
MS
1130
1131 gss_delete_sec_context(&minor_status, &context, GSS_C_NO_BUFFER);
07ed0e9a
MS
1132
1133# if defined(SO_PEERCRED) && defined(AF_LOCAL)
1134 /*
1135 * Get the client's UID if we are printing locally - that allows a backend
1136 * to run as the correct user to get Kerberos credentials of its own.
1137 */
1138
eac3a0a0 1139 if (_httpAddrFamily(con->http.hostaddr) == AF_LOCAL)
07ed0e9a
MS
1140 {
1141 cupsd_ucred_t peercred; /* Peer credentials */
1142 socklen_t peersize; /* Size of peer credentials */
1143
1144 peersize = sizeof(peercred);
1145
1146# ifdef __APPLE__
1147 if (getsockopt(con->http.fd, 0, LOCAL_PEERCRED, &peercred, &peersize))
1148# else
1149 if (getsockopt(con->http.fd, SOL_SOCKET, SO_PEERCRED, &peercred,
1150 &peersize))
1151# endif /* __APPLE__ */
1152 {
dcb445bc
MS
1153 cupsdLogMessage(CUPSD_LOG_ERROR,
1154 "[Client %d] Unable to get peer credentials - %s",
1155 con->http.fd, strerror(errno));
07ed0e9a
MS
1156 }
1157 else
1158 {
1159 cupsdLogMessage(CUPSD_LOG_DEBUG,
dcb445bc
MS
1160 "[Client %d] Using credentials for UID %d.",
1161 con->http.fd, CUPSD_UCRED_UID(peercred));
07ed0e9a
MS
1162 con->gss_uid = CUPSD_UCRED_UID(peercred);
1163 }
1164 }
1165# endif /* SO_PEERCRED && AF_LOCAL */
f7deaa1a 1166 }
1167#endif /* HAVE_GSSAPI */
2fb76298 1168 else
ef416fc2 1169 {
355e94dc 1170 char scheme[256]; /* Auth scheme... */
355e94dc
MS
1171
1172
1173 if (sscanf(authorization, "%255s", scheme) != 1)
1174 strcpy(scheme, "UNKNOWN");
1175
dcb445bc
MS
1176 cupsdLogMessage(CUPSD_LOG_ERROR,
1177 "[Client %d] Bad authentication data \"%s ...\"",
1178 con->http.fd, scheme);
ef416fc2 1179 return;
1180 }
1181
1182 /*
1183 * If we get here, then we were able to validate the username and
1184 * password - copy the validated username and password to the client
1185 * data and return...
1186 */
1187
1188 strlcpy(con->username, username, sizeof(con->username));
1189 strlcpy(con->password, password, sizeof(con->password));
ef416fc2 1190}
1191
1192
080811b1
MS
1193/*
1194 * 'cupsdCheckAccess()' - Check whether the given address is allowed to
1195 * access a location.
1196 */
1197
1198int /* O - 1 if allowed, 0 otherwise */
1199cupsdCheckAccess(
1200 unsigned ip[4], /* I - Client address */
1201 char *name, /* I - Client hostname */
1202 int namelen, /* I - Length of hostname */
1203 cupsd_location_t *loc) /* I - Location to check */
1204{
1205 int allow; /* 1 if allowed, 0 otherwise */
1206
1207
88f9aafc 1208 if (!_cups_strcasecmp(name, "localhost"))
080811b1
MS
1209 {
1210 /*
1211 * Access from localhost (127.0.0.1 or ::1) is always allowed...
1212 */
1213
1214 return (1);
1215 }
1216 else
1217 {
1218 /*
1219 * Do authorization checks on the domain/address...
1220 */
1221
1222 switch (loc->order_type)
1223 {
1224 default :
1225 allow = 0; /* anti-compiler-warning-code */
1226 break;
1227
5bd77a73 1228 case CUPSD_AUTH_ALLOW : /* Order Deny,Allow */
080811b1
MS
1229 allow = 1;
1230
10d09e33 1231 if (cupsdCheckAuth(ip, name, namelen, loc->deny))
080811b1
MS
1232 allow = 0;
1233
10d09e33 1234 if (cupsdCheckAuth(ip, name, namelen, loc->allow))
080811b1
MS
1235 allow = 1;
1236 break;
1237
5bd77a73 1238 case CUPSD_AUTH_DENY : /* Order Allow,Deny */
080811b1
MS
1239 allow = 0;
1240
10d09e33 1241 if (cupsdCheckAuth(ip, name, namelen, loc->allow))
080811b1
MS
1242 allow = 1;
1243
10d09e33 1244 if (cupsdCheckAuth(ip, name, namelen, loc->deny))
080811b1
MS
1245 allow = 0;
1246 break;
1247 }
1248 }
1249
1250 return (allow);
1251}
1252
1253
ef416fc2 1254/*
1255 * 'cupsdCheckAuth()' - Check authorization masks.
1256 */
1257
1258int /* O - 1 if mask matches, 0 otherwise */
10d09e33
MS
1259cupsdCheckAuth(unsigned ip[4], /* I - Client address */
1260 char *name, /* I - Client hostname */
1261 int name_len, /* I - Length of hostname */
1262 cups_array_t *masks) /* I - Masks */
ef416fc2 1263{
10d09e33
MS
1264 int i; /* Looping var */
1265 cupsd_authmask_t *mask; /* Current mask */
1266 cupsd_netif_t *iface; /* Network interface */
1267 unsigned netip4; /* IPv4 network address */
ef416fc2 1268#ifdef AF_INET6
10d09e33 1269 unsigned netip6[4]; /* IPv6 network address */
ef416fc2 1270#endif /* AF_INET6 */
1271
e07d4801 1272
10d09e33
MS
1273 for (mask = (cupsd_authmask_t *)cupsArrayFirst(masks);
1274 mask;
1275 mask = (cupsd_authmask_t *)cupsArrayNext(masks))
ef416fc2 1276 {
10d09e33 1277 switch (mask->type)
ef416fc2 1278 {
5bd77a73 1279 case CUPSD_AUTH_INTERFACE :
ef416fc2 1280 /*
1281 * Check for a match with a network interface...
1282 */
1283
1284 netip4 = htonl(ip[3]);
1285
1286#ifdef AF_INET6
1287 netip6[0] = htonl(ip[0]);
1288 netip6[1] = htonl(ip[1]);
1289 netip6[2] = htonl(ip[2]);
1290 netip6[3] = htonl(ip[3]);
1291#endif /* AF_INET6 */
1292
10d09e33 1293 if (!strcmp(mask->mask.name.name, "*"))
ef416fc2 1294 {
e07d4801
MS
1295#ifdef __APPLE__
1296 /*
1297 * Allow Back-to-My-Mac addresses...
1298 */
1299
1300 if ((ip[0] & 0xff000000) == 0xfd000000)
1301 return (1);
1302#endif /* __APPLE__ */
1303
ef416fc2 1304 /*
1305 * Check against all local interfaces...
1306 */
1307
1308 cupsdNetIFUpdate();
1309
e00b005a 1310 for (iface = (cupsd_netif_t *)cupsArrayFirst(NetIFList);
1311 iface;
1312 iface = (cupsd_netif_t *)cupsArrayNext(NetIFList))
ef416fc2 1313 {
1314 /*
1315 * Only check local interfaces...
1316 */
1317
1318 if (!iface->is_local)
1319 continue;
1320
1321 if (iface->address.addr.sa_family == AF_INET)
1322 {
1323 /*
1324 * Check IPv4 address...
1325 */
1326
1327 if ((netip4 & iface->mask.ipv4.sin_addr.s_addr) ==
1328 (iface->address.ipv4.sin_addr.s_addr &
1329 iface->mask.ipv4.sin_addr.s_addr))
1330 return (1);
1331 }
1332#ifdef AF_INET6
1333 else
1334 {
1335 /*
1336 * Check IPv6 address...
1337 */
1338
1339 for (i = 0; i < 4; i ++)
1340 if ((netip6[i] & iface->mask.ipv6.sin6_addr.s6_addr32[i]) !=
1341 (iface->address.ipv6.sin6_addr.s6_addr32[i] &
1342 iface->mask.ipv6.sin6_addr.s6_addr32[i]))
1343 break;
1344
1345 if (i == 4)
1346 return (1);
1347 }
1348#endif /* AF_INET6 */
1349 }
1350 }
1351 else
1352 {
1353 /*
1354 * Check the named interface...
1355 */
1356
e00b005a 1357 for (iface = (cupsd_netif_t *)cupsArrayFirst(NetIFList);
ed486911 1358 iface;
e00b005a 1359 iface = (cupsd_netif_t *)cupsArrayNext(NetIFList))
ef416fc2 1360 {
10d09e33 1361 if (strcmp(mask->mask.name.name, iface->name))
ed486911 1362 continue;
1363
ef416fc2 1364 if (iface->address.addr.sa_family == AF_INET)
1365 {
1366 /*
1367 * Check IPv4 address...
1368 */
1369
1370 if ((netip4 & iface->mask.ipv4.sin_addr.s_addr) ==
1371 (iface->address.ipv4.sin_addr.s_addr &
1372 iface->mask.ipv4.sin_addr.s_addr))
1373 return (1);
1374 }
1375#ifdef AF_INET6
1376 else
1377 {
1378 /*
1379 * Check IPv6 address...
1380 */
1381
1382 for (i = 0; i < 4; i ++)
1383 if ((netip6[i] & iface->mask.ipv6.sin6_addr.s6_addr32[i]) !=
1384 (iface->address.ipv6.sin6_addr.s6_addr32[i] &
1385 iface->mask.ipv6.sin6_addr.s6_addr32[i]))
1386 break;
1387
1388 if (i == 4)
1389 return (1);
1390 }
1391#endif /* AF_INET6 */
1392 }
1393 }
1394 break;
1395
5bd77a73 1396 case CUPSD_AUTH_NAME :
ef416fc2 1397 /*
1398 * Check for exact name match...
1399 */
1400
88f9aafc 1401 if (!_cups_strcasecmp(name, mask->mask.name.name))
ef416fc2 1402 return (1);
1403
1404 /*
1405 * Check for domain match...
1406 */
1407
10d09e33
MS
1408 if (name_len >= mask->mask.name.length &&
1409 mask->mask.name.name[0] == '.' &&
88f9aafc 1410 !_cups_strcasecmp(name + name_len - mask->mask.name.length,
10d09e33 1411 mask->mask.name.name))
ef416fc2 1412 return (1);
1413 break;
1414
5bd77a73 1415 case CUPSD_AUTH_IP :
ef416fc2 1416 /*
1417 * Check for IP/network address match...
1418 */
1419
1420 for (i = 0; i < 4; i ++)
10d09e33
MS
1421 if ((ip[i] & mask->mask.ip.netmask[i]) !=
1422 mask->mask.ip.address[i])
ef416fc2 1423 break;
1424
1425 if (i == 4)
1426 return (1);
1427 break;
1428 }
ef416fc2 1429 }
1430
1431 return (0);
1432}
1433
1434
1435/*
1436 * 'cupsdCheckGroup()' - Check for a user's group membership.
1437 */
1438
1439int /* O - 1 if user is a member, 0 otherwise */
1440cupsdCheckGroup(
1441 const char *username, /* I - User name */
1442 struct passwd *user, /* I - System user info */
1443 const char *groupname) /* I - Group name */
1444{
8922323b
MS
1445 int i; /* Looping var */
1446 struct group *group; /* System group info */
1447 char junk[33]; /* MD5 password (not used) */
bd7854cb 1448#ifdef HAVE_MBR_UID_TO_UUID
8922323b
MS
1449 uuid_t useruuid, /* UUID for username */
1450 groupuuid; /* UUID for groupname */
1451 int is_member; /* True if user is a member of group */
bd7854cb 1452#endif /* HAVE_MBR_UID_TO_UUID */
ef416fc2 1453
1454
1455 cupsdLogMessage(CUPSD_LOG_DEBUG2,
1456 "cupsdCheckGroup(username=\"%s\", user=%p, groupname=\"%s\")",
1457 username, user, groupname);
1458
1459 /*
1460 * Validate input...
1461 */
1462
1463 if (!username || !groupname)
1464 return (0);
1465
1466 /*
1467 * Check to see if the user is a member of the named group...
1468 */
1469
1470 group = getgrnam(groupname);
1471 endgrent();
1472
1473 if (group != NULL)
1474 {
1475 /*
1476 * Group exists, check it...
1477 */
1478
1479 for (i = 0; group->gr_mem[i]; i ++)
88f9aafc 1480 if (!_cups_strcasecmp(username, group->gr_mem[i]))
ef416fc2 1481 return (1);
1482 }
1483
1484 /*
1485 * Group doesn't exist or user not in group list, check the group ID
1486 * against the user's group ID...
1487 */
1488
1489 if (user && group && group->gr_gid == user->pw_gid)
1490 return (1);
1491
bd7854cb 1492#ifdef HAVE_MBR_UID_TO_UUID
1493 /*
1494 * Check group membership through MacOS X membership API...
1495 */
1496
dd1abb6b 1497 if (user && !mbr_uid_to_uuid(user->pw_uid, useruuid))
c9fc04c6 1498 {
8922323b
MS
1499 if (group)
1500 {
1501 /*
1502 * Map group name to UUID and check membership...
1503 */
c9fc04c6 1504
8922323b
MS
1505 if (!mbr_gid_to_uuid(group->gr_gid, groupuuid))
1506 if (!mbr_check_membership(useruuid, groupuuid, &is_member))
1507 if (is_member)
1508 return (1);
1509 }
1510 else if (groupname[0] == '#')
1511 {
1512 /*
1513 * Use UUID directly and check for equality (user UUID) and
1514 * membership (group UUID)...
1515 */
1516
1517 if (!uuid_parse((char *)groupname + 1, groupuuid))
1518 {
1519 if (!uuid_compare(useruuid, groupuuid))
c9fc04c6 1520 return (1);
8922323b
MS
1521 else if (!mbr_check_membership(useruuid, groupuuid, &is_member))
1522 if (is_member)
1523 return (1);
1524 }
1525
1526 return (0);
1527 }
1528 }
1529 else if (groupname[0] == '#')
1530 return (0);
bd7854cb 1531#endif /* HAVE_MBR_UID_TO_UUID */
1532
ef416fc2 1533 /*
1534 * Username not found, group not found, or user is not part of the
1535 * system group... Check for a user and group in the MD5 password
1536 * file...
1537 */
1538
e1d6a774 1539 if (get_md5_password(username, groupname, junk) != NULL)
ef416fc2 1540 return (1);
1541
1542 /*
1543 * If we get this far, then the user isn't part of the named group...
1544 */
1545
1546 return (0);
1547}
1548
1549
1550/*
1551 * 'cupsdCopyLocation()' - Make a copy of a location...
1552 */
1553
1554cupsd_location_t * /* O - New location */
1555cupsdCopyLocation(
10d09e33 1556 cupsd_location_t *loc) /* I - Original location */
ef416fc2 1557{
ef416fc2 1558 cupsd_location_t *temp; /* New location */
ef416fc2 1559
1560
ef416fc2 1561 /*
10d09e33 1562 * Make a copy of the original location...
ef416fc2 1563 */
1564
10d09e33 1565 if ((temp = calloc(1, sizeof(cupsd_location_t))) == NULL)
ef416fc2 1566 return (NULL);
1567
ef416fc2 1568 /*
1569 * Copy the information from the original location to the new one.
1570 */
1571
10d09e33
MS
1572 if (!loc)
1573 return (temp);
ef416fc2 1574
10d09e33
MS
1575 if (loc->location)
1576 temp->location = _cupsStrAlloc(loc->location);
ef416fc2 1577
10d09e33
MS
1578 temp->limit = loc->limit;
1579 temp->order_type = loc->order_type;
1580 temp->type = loc->type;
1581 temp->level = loc->level;
1582 temp->satisfy = loc->satisfy;
1583 temp->encryption = loc->encryption;
1584
1585 if (loc->names)
1586 {
1587 if ((temp->names = cupsArrayDup(loc->names)) == NULL)
ef416fc2 1588 {
1589 cupsdLogMessage(CUPSD_LOG_ERROR,
10d09e33
MS
1590 "Unable to allocate memory for %d names: %s",
1591 cupsArrayCount(loc->names), strerror(errno));
bd7854cb 1592
10d09e33 1593 cupsdFreeLocation(temp);
ef416fc2 1594 return (NULL);
1595 }
ef416fc2 1596 }
1597
10d09e33 1598 if (loc->allow)
ef416fc2 1599 {
1600 /*
1601 * Copy allow rules...
1602 */
1603
10d09e33 1604 if ((temp->allow = cupsArrayDup(loc->allow)) == NULL)
ef416fc2 1605 {
1606 cupsdLogMessage(CUPSD_LOG_ERROR,
10d09e33
MS
1607 "Unable to allocate memory for %d allow rules: %s",
1608 cupsArrayCount(loc->allow), strerror(errno));
1609 cupsdFreeLocation(temp);
ef416fc2 1610 return (NULL);
1611 }
ef416fc2 1612 }
1613
10d09e33 1614 if (loc->deny)
ef416fc2 1615 {
1616 /*
1617 * Copy deny rules...
1618 */
1619
10d09e33 1620 if ((temp->deny = cupsArrayDup(loc->deny)) == NULL)
ef416fc2 1621 {
1622 cupsdLogMessage(CUPSD_LOG_ERROR,
10d09e33
MS
1623 "Unable to allocate memory for %d deny rules: %s",
1624 cupsArrayCount(loc->deny), strerror(errno));
1625 cupsdFreeLocation(temp);
ef416fc2 1626 return (NULL);
1627 }
ef416fc2 1628 }
1629
1630 return (temp);
1631}
1632
1633
1634/*
1635 * 'cupsdDeleteAllLocations()' - Free all memory used for location authorization.
1636 */
1637
1638void
1639cupsdDeleteAllLocations(void)
1640{
ef416fc2 1641 /*
10d09e33 1642 * Free the location array, which will free all of the locations...
ef416fc2 1643 */
1644
bd7854cb 1645 cupsArrayDelete(Locations);
1646 Locations = NULL;
ef416fc2 1647}
1648
1649
ef416fc2 1650/*
1651 * 'cupsdFindBest()' - Find the location entry that best matches the resource.
1652 */
1653
1654cupsd_location_t * /* O - Location that matches */
1655cupsdFindBest(const char *path, /* I - Resource path */
1656 http_state_t state) /* I - HTTP state/request */
1657{
ef416fc2 1658 char uri[HTTP_MAX_URI],
1659 /* URI in request... */
1660 *uriptr; /* Pointer into URI */
1661 cupsd_location_t *loc, /* Current location */
1662 *best; /* Best match for location so far */
1663 int bestlen; /* Length of best match */
1664 int limit; /* Limit field */
5bd77a73 1665 static const int limits[] = /* Map http_status_t to CUPSD_AUTH_LIMIT_xyz */
ef416fc2 1666 {
5bd77a73
MS
1667 CUPSD_AUTH_LIMIT_ALL,
1668 CUPSD_AUTH_LIMIT_OPTIONS,
1669 CUPSD_AUTH_LIMIT_GET,
1670 CUPSD_AUTH_LIMIT_GET,
1671 CUPSD_AUTH_LIMIT_HEAD,
1672 CUPSD_AUTH_LIMIT_POST,
1673 CUPSD_AUTH_LIMIT_POST,
1674 CUPSD_AUTH_LIMIT_POST,
1675 CUPSD_AUTH_LIMIT_PUT,
1676 CUPSD_AUTH_LIMIT_PUT,
1677 CUPSD_AUTH_LIMIT_DELETE,
1678 CUPSD_AUTH_LIMIT_TRACE,
1679 CUPSD_AUTH_LIMIT_ALL,
1680 CUPSD_AUTH_LIMIT_ALL
ef416fc2 1681 };
1682
1683
1684 /*
1685 * First copy the connection URI to a local string so we have drop
1686 * any .ppd extension from the pathname in /printers or /classes
1687 * URIs...
1688 */
1689
1690 strlcpy(uri, path, sizeof(uri));
1691
1692 if (!strncmp(uri, "/printers/", 10) ||
1693 !strncmp(uri, "/classes/", 9))
1694 {
1695 /*
1696 * Check if the URI has .ppd on the end...
1697 */
1698
1699 uriptr = uri + strlen(uri) - 4; /* len > 4 if we get here... */
1700
1701 if (!strcmp(uriptr, ".ppd"))
1702 *uriptr = '\0';
1703 }
1704
1705 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdFindBest: uri = \"%s\"...", uri);
1706
1707 /*
1708 * Loop through the list of locations to find a match...
1709 */
1710
1711 limit = limits[state];
1712 best = NULL;
1713 bestlen = 0;
1714
bd7854cb 1715 for (loc = (cupsd_location_t *)cupsArrayFirst(Locations);
1716 loc;
1717 loc = (cupsd_location_t *)cupsArrayNext(Locations))
ef416fc2 1718 {
1719 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdFindBest: Location %s Limit %x",
e1d6a774 1720 loc->location ? loc->location : "nil", loc->limit);
ef416fc2 1721
1722 if (!strncmp(uri, "/printers/", 10) || !strncmp(uri, "/classes/", 9))
1723 {
1724 /*
1725 * Use case-insensitive comparison for queue names...
1726 */
1727
e1d6a774 1728 if (loc->length > bestlen && loc->location &&
88f9aafc 1729 !_cups_strncasecmp(uri, loc->location, loc->length) &&
ef416fc2 1730 loc->location[0] == '/' &&
1731 (limit & loc->limit) != 0)
1732 {
1733 best = loc;
1734 bestlen = loc->length;
1735 }
1736 }
1737 else
1738 {
1739 /*
1740 * Use case-sensitive comparison for other URIs...
1741 */
1742
e1d6a774 1743 if (loc->length > bestlen && loc->location &&
ef416fc2 1744 !strncmp(uri, loc->location, loc->length) &&
1745 loc->location[0] == '/' &&
1746 (limit & loc->limit) != 0)
1747 {
1748 best = loc;
1749 bestlen = loc->length;
1750 }
1751 }
1752 }
1753
1754 /*
1755 * Return the match, if any...
1756 */
1757
1758 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdFindBest: best = %s",
1759 best ? best->location : "NONE");
1760
1761 return (best);
1762}
1763
1764
1765/*
1766 * 'cupsdFindLocation()' - Find the named location.
1767 */
1768
1769cupsd_location_t * /* O - Location that matches */
1770cupsdFindLocation(const char *location) /* I - Connection */
1771{
bd7854cb 1772 cupsd_location_t key; /* Search key */
ef416fc2 1773
1774
bd7854cb 1775 key.location = (char *)location;
ef416fc2 1776
bd7854cb 1777 return ((cupsd_location_t *)cupsArrayFind(Locations, &key));
ef416fc2 1778}
1779
1780
10d09e33
MS
1781/*
1782 * 'cupsdFreeLocation()' - Free all memory used by a location.
1783 */
1784
1785void
1786cupsdFreeLocation(cupsd_location_t *loc)/* I - Location to free */
1787{
1788 cupsArrayDelete(loc->names);
1789 cupsArrayDelete(loc->allow);
1790 cupsArrayDelete(loc->deny);
1791
1792 _cupsStrFree(loc->location);
1793 free(loc);
1794}
1795
1796
ef416fc2 1797/*
1798 * 'cupsdIsAuthorized()' - Check to see if the user is authorized...
1799 */
1800
1801http_status_t /* O - HTTP_OK if authorized or error code */
1802cupsdIsAuthorized(cupsd_client_t *con, /* I - Connection */
1803 const char *owner)/* I - Owner of object */
1804{
10d09e33 1805 int i, /* Looping vars */
2fb76298
MS
1806 auth, /* Authorization status */
1807 type; /* Type of authentication */
ef416fc2 1808 unsigned address[4]; /* Authorization address */
1809 cupsd_location_t *best; /* Best match for location so far */
1810 int hostlen; /* Length of hostname */
10d09e33
MS
1811 char *name, /* Current username */
1812 username[256], /* Username to authorize */
db1f069b
MS
1813 ownername[256], /* Owner name to authorize */
1814 *ptr; /* Pointer into username */
ef416fc2 1815 struct passwd *pw; /* User password data */
1816 static const char * const levels[] = /* Auth levels */
1817 {
1818 "ANON",
1819 "USER",
1820 "GROUP"
1821 };
1822 static const char * const types[] = /* Auth types */
1823 {
2fb76298
MS
1824 "None",
1825 "Basic",
1826 "Digest",
1827 "BasicDigest",
1828 "Negotiate"
ef416fc2 1829 };
1830
1831
1832 cupsdLogMessage(CUPSD_LOG_DEBUG2,
1833 "cupsdIsAuthorized: con->uri=\"%s\", con->best=%p(%s)",
f301802f 1834 con->uri, con->best, con->best ? con->best->location ?
1835 con->best->location : "(null)" : "");
fa73b229 1836 if (owner)
1837 cupsdLogMessage(CUPSD_LOG_DEBUG2,
1838 "cupsdIsAuthorized: owner=\"%s\"", owner);
ef416fc2 1839
1840 /*
1841 * If there is no "best" authentication rule for this request, then
1842 * access is allowed from the local system and denied from other
1843 * addresses...
1844 */
1845
1846 if (!con->best)
1847 {
1848 if (!strcmp(con->http.hostname, "localhost") ||
1849 !strcmp(con->http.hostname, ServerName))
1850 return (HTTP_OK);
1851 else
1852 return (HTTP_FORBIDDEN);
1853 }
1854
1855 best = con->best;
1856
5bd77a73 1857 if ((type = best->type) == CUPSD_AUTH_DEFAULT)
dcb445bc 1858 type = cupsdDefaultAuthType();
2fb76298 1859
ef416fc2 1860 cupsdLogMessage(CUPSD_LOG_DEBUG2,
5bd77a73
MS
1861 "cupsdIsAuthorized: level=CUPSD_AUTH_%s, type=%s, "
1862 "satisfy=CUPSD_AUTH_SATISFY_%s, num_names=%d",
2fb76298 1863 levels[best->level], types[type],
10d09e33 1864 best->satisfy ? "ANY" : "ALL", cupsArrayCount(best->names));
ef416fc2 1865
5bd77a73 1866 if (best->limit == CUPSD_AUTH_LIMIT_IPP)
ef416fc2 1867 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: op=%x(%s)",
1868 best->op, ippOpString(best->op));
1869
1870 /*
1871 * Check host/ip-based accesses...
1872 */
1873
1874#ifdef AF_INET6
1875 if (con->http.hostaddr->addr.sa_family == AF_INET6)
1876 {
1877 /*
1878 * Copy IPv6 address...
1879 */
1880
1881 address[0] = ntohl(con->http.hostaddr->ipv6.sin6_addr.s6_addr32[0]);
1882 address[1] = ntohl(con->http.hostaddr->ipv6.sin6_addr.s6_addr32[1]);
1883 address[2] = ntohl(con->http.hostaddr->ipv6.sin6_addr.s6_addr32[2]);
1884 address[3] = ntohl(con->http.hostaddr->ipv6.sin6_addr.s6_addr32[3]);
1885 }
1886 else
1887#endif /* AF_INET6 */
1888 if (con->http.hostaddr->addr.sa_family == AF_INET)
1889 {
1890 /*
1891 * Copy IPv4 address...
1892 */
1893
1894 address[0] = 0;
1895 address[1] = 0;
1896 address[2] = 0;
1897 address[3] = ntohl(con->http.hostaddr->ipv4.sin_addr.s_addr);
1898 }
1899 else
1900 memset(address, 0, sizeof(address));
1901
1902 hostlen = strlen(con->http.hostname);
1903
080811b1 1904 auth = cupsdCheckAccess(address, con->http.hostname, hostlen, best)
5bd77a73 1905 ? CUPSD_AUTH_ALLOW : CUPSD_AUTH_DENY;
ef416fc2 1906
5bd77a73 1907 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: auth=CUPSD_AUTH_%s...",
ef416fc2 1908 auth ? "DENY" : "ALLOW");
1909
5bd77a73 1910 if (auth == CUPSD_AUTH_DENY && best->satisfy == CUPSD_AUTH_SATISFY_ALL)
ef416fc2 1911 return (HTTP_FORBIDDEN);
1912
1913#ifdef HAVE_SSL
1914 /*
1915 * See if encryption is required...
1916 */
1917
f7deaa1a 1918 if ((best->encryption >= HTTP_ENCRYPT_REQUIRED && !con->http.tls &&
88f9aafc 1919 _cups_strcasecmp(con->http.hostname, "localhost") &&
5bd77a73 1920 best->satisfy == CUPSD_AUTH_SATISFY_ALL) &&
c8fef167 1921 !(type == CUPSD_AUTH_NEGOTIATE ||
dcb445bc
MS
1922 (type == CUPSD_AUTH_NONE &&
1923 cupsdDefaultAuthType() == CUPSD_AUTH_NEGOTIATE)))
ef416fc2 1924 {
355e94dc 1925 cupsdLogMessage(CUPSD_LOG_DEBUG,
ef416fc2 1926 "cupsdIsAuthorized: Need upgrade to TLS...");
1927 return (HTTP_UPGRADE_REQUIRED);
1928 }
1929#endif /* HAVE_SSL */
1930
1931 /*
1932 * Now see what access level is required...
1933 */
1934
5bd77a73 1935 if (best->level == CUPSD_AUTH_ANON || /* Anonymous access - allow it */
10d09e33 1936 (type == CUPSD_AUTH_NONE && cupsArrayCount(best->names) == 0))
ef416fc2 1937 return (HTTP_OK);
1938
5bd77a73
MS
1939 if (!con->username[0] && type == CUPSD_AUTH_NONE &&
1940 best->limit == CUPSD_AUTH_LIMIT_IPP)
ef416fc2 1941 {
1942 /*
1943 * Check for unauthenticated username...
1944 */
1945
1946 ipp_attribute_t *attr; /* requesting-user-name attribute */
1947
1948
1949 attr = ippFindAttribute(con->request, "requesting-user-name", IPP_TAG_NAME);
1950 if (attr)
1951 {
355e94dc 1952 cupsdLogMessage(CUPSD_LOG_DEBUG,
ef416fc2 1953 "cupsdIsAuthorized: requesting-user-name=\"%s\"",
1954 attr->values[0].string.text);
db1f069b 1955 strlcpy(username, attr->values[0].string.text, sizeof(username));
ef416fc2 1956 }
5bd77a73 1957 else if (best->satisfy == CUPSD_AUTH_SATISFY_ALL || auth == CUPSD_AUTH_DENY)
ef416fc2 1958 return (HTTP_UNAUTHORIZED); /* Non-anonymous needs user/pass */
1959 else
1960 return (HTTP_OK); /* unless overridden with Satisfy */
1961 }
fa73b229 1962 else
1963 {
355e94dc 1964 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdIsAuthorized: username=\"%s\"",
fa73b229 1965 con->username);
1966
f7deaa1a 1967#ifdef HAVE_AUTHORIZATION_H
1968 if (!con->username[0] && !con->authref)
1969#else
fa73b229 1970 if (!con->username[0])
f7deaa1a 1971#endif /* HAVE_AUTHORIZATION_H */
fa73b229 1972 {
5bd77a73 1973 if (best->satisfy == CUPSD_AUTH_SATISFY_ALL || auth == CUPSD_AUTH_DENY)
fa73b229 1974 return (HTTP_UNAUTHORIZED); /* Non-anonymous needs user/pass */
1975 else
1976 return (HTTP_OK); /* unless overridden with Satisfy */
1977 }
1978
eac3a0a0 1979
5bd77a73 1980 if (con->type != type && type != CUPSD_AUTH_NONE &&
eac3a0a0
MS
1981#ifdef HAVE_GSSAPI
1982 (type != CUPSD_AUTH_NEGOTIATE || con->gss_uid <= 0) &&
1983#endif /* HAVE_GSSAPI */
5bd77a73 1984 (con->type != CUPSD_AUTH_BASIC || type != CUPSD_AUTH_BASICDIGEST))
2fb76298
MS
1985 {
1986 cupsdLogMessage(CUPSD_LOG_ERROR, "Authorized using %s, expected %s!",
1987 types[con->type], types[type]);
1988
1989 return (HTTP_UNAUTHORIZED);
1990 }
1991
db1f069b 1992 strlcpy(username, con->username, sizeof(username));
fa73b229 1993 }
ef416fc2 1994
1995 /*
fa73b229 1996 * OK, got a username. See if we need normal user access, or group
ef416fc2 1997 * access... (root always matches)
1998 */
1999
fa73b229 2000 if (!strcmp(username, "root"))
ef416fc2 2001 return (HTTP_OK);
2002
db1f069b
MS
2003 /*
2004 * Strip any @domain or @KDC from the username and owner...
2005 */
2006
2007 if ((ptr = strchr(username, '@')) != NULL)
2008 *ptr = '\0';
2009
2010 if (owner)
2011 {
2012 strlcpy(ownername, owner, sizeof(ownername));
2013
2014 if ((ptr = strchr(ownername, '@')) != NULL)
2015 *ptr = '\0';
2016 }
2017 else
2018 ownername[0] = '\0';
2019
ef416fc2 2020 /*
2021 * Get the user info...
2022 */
2023
f7deaa1a 2024 if (username[0])
2025 {
2026 pw = getpwnam(username);
2027 endpwent();
2028 }
2029 else
2030 pw = NULL;
ef416fc2 2031
5bd77a73 2032 if (best->level == CUPSD_AUTH_USER)
ef416fc2 2033 {
2034 /*
2035 * If there are no names associated with this location, then
2036 * any valid user is OK...
2037 */
2038
10d09e33 2039 if (cupsArrayCount(best->names) == 0)
ef416fc2 2040 return (HTTP_OK);
2041
2042 /*
2043 * Otherwise check the user list and return OK if this user is
2044 * allowed...
2045 */
2046
2047 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2048 "cupsdIsAuthorized: Checking user membership...");
2049
f7deaa1a 2050#ifdef HAVE_AUTHORIZATION_H
2051 /*
2052 * If an authorization reference was supplied it must match a right name...
2053 */
2054
2055 if (con->authref)
2056 {
10d09e33
MS
2057 for (name = (char *)cupsArrayFirst(best->names);
2058 name;
2059 name = (char *)cupsArrayNext(best->names))
f7deaa1a 2060 {
88f9aafc 2061 if (!_cups_strncasecmp(name, "@AUTHKEY(", 9) && check_authref(con, name + 9))
f7deaa1a 2062 return (HTTP_OK);
88f9aafc 2063 else if (!_cups_strcasecmp(name, "@SYSTEM") && SystemGroupAuthKey &&
f7deaa1a 2064 check_authref(con, SystemGroupAuthKey))
2065 return (HTTP_OK);
2066 }
2067
ee571f26 2068 return (HTTP_FORBIDDEN);
f7deaa1a 2069 }
2070#endif /* HAVE_AUTHORIZATION_H */
2071
10d09e33
MS
2072 for (name = (char *)cupsArrayFirst(best->names);
2073 name;
2074 name = (char *)cupsArrayNext(best->names))
ef416fc2 2075 {
88f9aafc
MS
2076 if (!_cups_strcasecmp(name, "@OWNER") && owner &&
2077 !_cups_strcasecmp(username, ownername))
ef416fc2 2078 return (HTTP_OK);
88f9aafc 2079 else if (!_cups_strcasecmp(name, "@SYSTEM"))
ef416fc2 2080 {
10d09e33
MS
2081 for (i = 0; i < NumSystemGroups; i ++)
2082 if (cupsdCheckGroup(username, pw, SystemGroups[i]))
ef416fc2 2083 return (HTTP_OK);
2084 }
10d09e33 2085 else if (name[0] == '@')
ef416fc2 2086 {
10d09e33 2087 if (cupsdCheckGroup(username, pw, name + 1))
ef416fc2 2088 return (HTTP_OK);
2089 }
88f9aafc 2090 else if (!_cups_strcasecmp(username, name))
ef416fc2 2091 return (HTTP_OK);
2092 }
2093
85dda01c 2094 return (con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED);
ef416fc2 2095 }
2096
2097 /*
2098 * Check to see if this user is in any of the named groups...
2099 */
2100
2101 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2102 "cupsdIsAuthorized: Checking group membership...");
2103
2104 /*
2105 * Check to see if this user is in any of the named groups...
2106 */
2107
10d09e33
MS
2108 for (name = (char *)cupsArrayFirst(best->names);
2109 name;
2110 name = (char *)cupsArrayNext(best->names))
ef416fc2 2111 {
2112 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2113 "cupsdIsAuthorized: Checking group \"%s\" membership...",
10d09e33 2114 name);
ef416fc2 2115
88f9aafc 2116 if (!_cups_strcasecmp(name, "@SYSTEM"))
ef416fc2 2117 {
10d09e33
MS
2118 for (i = 0; i < NumSystemGroups; i ++)
2119 if (cupsdCheckGroup(username, pw, SystemGroups[i]))
ef416fc2 2120 return (HTTP_OK);
2121 }
10d09e33 2122 else if (cupsdCheckGroup(username, pw, name))
ef416fc2 2123 return (HTTP_OK);
2124 }
2125
2126 /*
2127 * The user isn't part of the specified group, so deny access...
2128 */
2129
355e94dc 2130 cupsdLogMessage(CUPSD_LOG_DEBUG,
ef416fc2 2131 "cupsdIsAuthorized: User not in group(s)!");
2132
85dda01c 2133 return (con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED);
ef416fc2 2134}
2135
2136
2137/*
10d09e33
MS
2138 * 'cupsdNewLocation()' - Create a new location for authorization.
2139 *
2140 * Note: Still need to call cupsdAddLocation() to add it to the list of global
2141 * locations.
ef416fc2 2142 */
2143
10d09e33
MS
2144cupsd_location_t * /* O - Pointer to new location record */
2145cupsdNewLocation(const char *location) /* I - Location path */
ef416fc2 2146{
10d09e33 2147 cupsd_location_t *temp; /* New location */
ef416fc2 2148
2149
2150 /*
10d09e33 2151 * Try to allocate memory for the new location.
ef416fc2 2152 */
2153
10d09e33 2154 if ((temp = calloc(1, sizeof(cupsd_location_t))) == NULL)
ef416fc2 2155 return (NULL);
2156
2157 /*
10d09e33 2158 * Initialize the record and copy the name over...
ef416fc2 2159 */
2160
10d09e33
MS
2161 if ((temp->location = _cupsStrAlloc(location)) == NULL)
2162 {
2163 free(temp);
ef416fc2 2164 return (NULL);
10d09e33 2165 }
ef416fc2 2166
10d09e33 2167 temp->length = strlen(temp->location);
ef416fc2 2168
2169 /*
10d09e33 2170 * Return the new record...
ef416fc2 2171 */
2172
ef416fc2 2173 return (temp);
2174}
2175
2176
f7deaa1a 2177#ifdef HAVE_AUTHORIZATION_H
2178/*
2179 * 'check_authref()' - Check if an authorization services reference has the
2180 * supplied right.
2181 */
2182
2183static int /* O - 1 if right is valid, 0 otherwise */
2184check_authref(cupsd_client_t *con, /* I - Connection */
2185 const char *right) /* I - Right name */
2186{
2187 OSStatus status; /* OS Status */
2188 AuthorizationItem authright; /* Authorization right */
2189 AuthorizationRights authrights; /* Authorization rights */
2190 AuthorizationFlags authflags; /* Authorization flags */
2191
2192
2193 /*
2194 * Check to see if the user is allowed to perform the task...
2195 */
2196
2197 if (!con->authref)
2198 return (0);
2199
2200 authright.name = right;
2201 authright.valueLength = 0;
2202 authright.value = NULL;
2203 authright.flags = 0;
2204
2205 authrights.count = 1;
2206 authrights.items = &authright;
2207
c8fef167 2208 authflags = kAuthorizationFlagDefaults |
f7deaa1a 2209 kAuthorizationFlagExtendRights;
2210
c8fef167
MS
2211 if ((status = AuthorizationCopyRights(con->authref, &authrights,
2212 kAuthorizationEmptyEnvironment,
f7deaa1a 2213 authflags, NULL)) != 0)
2214 {
2215 cupsdLogMessage(CUPSD_LOG_ERROR,
2216 "AuthorizationCopyRights(\"%s\") returned %d (%s)",
2217 authright.name, (int)status, cssmErrorString(status));
2218 return (0);
2219 }
2220
2221 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2222 "AuthorizationCopyRights(\"%s\") succeeded!",
2223 authright.name);
2224
2225 return (1);
2226}
2227#endif /* HAVE_AUTHORIZATION_H */
2228
2229
bd7854cb 2230/*
2231 * 'compare_locations()' - Compare two locations.
2232 */
2233
2234static int /* O - Result of comparison */
2235compare_locations(cupsd_location_t *a, /* I - First location */
2236 cupsd_location_t *b) /* I - Second location */
2237{
2238 return (strcmp(b->location, a->location));
2239}
2240
2241
10d09e33
MS
2242/*
2243 * 'copy_authmask()' - Copy function for auth masks.
2244 */
2245
2246static cupsd_authmask_t * /* O - New auth mask */
2247copy_authmask(cupsd_authmask_t *mask, /* I - Existing auth mask */
2248 void *data) /* I - User data (unused) */
2249{
2250 cupsd_authmask_t *temp; /* New auth mask */
2251
2252
2253 (void)data;
2254
2255 if ((temp = malloc(sizeof(cupsd_authmask_t))) != NULL)
2256 {
2257 memcpy(temp, mask, sizeof(cupsd_authmask_t));
2258
2259 if (temp->type == CUPSD_AUTH_NAME || temp->type == CUPSD_AUTH_INTERFACE)
2260 {
2261 /*
2262 * Make a copy of the name...
2263 */
2264
2265 if ((temp->mask.name.name = _cupsStrAlloc(temp->mask.name.name)) == NULL)
2266 {
2267 /*
2268 * Failed to make copy...
2269 */
2270
2271 free(temp);
2272 temp = NULL;
2273 }
2274 }
2275 }
2276
2277 return (temp);
2278}
2279
2280
d09495fa 2281#if !HAVE_LIBPAM && !defined(HAVE_USERSEC_H)
ef416fc2 2282/*
2283 * 'cups_crypt()' - Encrypt the password using the DES or MD5 algorithms,
2284 * as needed.
2285 */
2286
2287static char * /* O - Encrypted password */
2288cups_crypt(const char *pw, /* I - Password string */
2289 const char *salt) /* I - Salt (key) string */
2290{
bd7854cb 2291 if (!strncmp(salt, "$1$", 3))
ef416fc2 2292 {
2293 /*
2294 * Use MD5 passwords without the benefit of PAM; this is for
2295 * Slackware Linux, and the algorithm was taken from the
2296 * old shadow-19990827/lib/md5crypt.c source code... :(
2297 */
2298
2299 int i; /* Looping var */
2300 unsigned long n; /* Output number */
2301 int pwlen; /* Length of password string */
2302 const char *salt_end; /* End of "salt" data for MD5 */
2303 char *ptr; /* Pointer into result string */
2304 _cups_md5_state_t state; /* Primary MD5 state info */
2305 _cups_md5_state_t state2; /* Secondary MD5 state info */
2306 unsigned char digest[16]; /* MD5 digest result */
2307 static char result[120]; /* Final password string */
2308
2309
2310 /*
2311 * Get the salt data between dollar signs, e.g. $1$saltdata$md5.
2312 * Get a maximum of 8 characters of salt data after $1$...
2313 */
2314
2315 for (salt_end = salt + 3; *salt_end && (salt_end - salt) < 11; salt_end ++)
2316 if (*salt_end == '$')
2317 break;
2318
2319 /*
2320 * Compute the MD5 sum we need...
2321 */
2322
2323 pwlen = strlen(pw);
2324
757d2cad 2325 _cupsMD5Init(&state);
2326 _cupsMD5Append(&state, (unsigned char *)pw, pwlen);
2327 _cupsMD5Append(&state, (unsigned char *)salt, salt_end - salt);
ef416fc2 2328
757d2cad 2329 _cupsMD5Init(&state2);
2330 _cupsMD5Append(&state2, (unsigned char *)pw, pwlen);
2331 _cupsMD5Append(&state2, (unsigned char *)salt + 3, salt_end - salt - 3);
2332 _cupsMD5Append(&state2, (unsigned char *)pw, pwlen);
2333 _cupsMD5Finish(&state2, digest);
ef416fc2 2334
2335 for (i = pwlen; i > 0; i -= 16)
757d2cad 2336 _cupsMD5Append(&state, digest, i > 16 ? 16 : i);
ef416fc2 2337
2338 for (i = pwlen; i > 0; i >>= 1)
757d2cad 2339 _cupsMD5Append(&state, (unsigned char *)((i & 1) ? "" : pw), 1);
ef416fc2 2340
757d2cad 2341 _cupsMD5Finish(&state, digest);
ef416fc2 2342
2343 for (i = 0; i < 1000; i ++)
2344 {
757d2cad 2345 _cupsMD5Init(&state);
ef416fc2 2346
2347 if (i & 1)
757d2cad 2348 _cupsMD5Append(&state, (unsigned char *)pw, pwlen);
ef416fc2 2349 else
757d2cad 2350 _cupsMD5Append(&state, digest, 16);
ef416fc2 2351
2352 if (i % 3)
757d2cad 2353 _cupsMD5Append(&state, (unsigned char *)salt + 3, salt_end - salt - 3);
ef416fc2 2354
2355 if (i % 7)
757d2cad 2356 _cupsMD5Append(&state, (unsigned char *)pw, pwlen);
ef416fc2 2357
2358 if (i & 1)
757d2cad 2359 _cupsMD5Append(&state, digest, 16);
ef416fc2 2360 else
757d2cad 2361 _cupsMD5Append(&state, (unsigned char *)pw, pwlen);
ef416fc2 2362
757d2cad 2363 _cupsMD5Finish(&state, digest);
ef416fc2 2364 }
2365
2366 /*
2367 * Copy the final sum to the result string and return...
2368 */
2369
2370 memcpy(result, salt, salt_end - salt);
2371 ptr = result + (salt_end - salt);
2372 *ptr++ = '$';
2373
2374 for (i = 0; i < 5; i ++, ptr += 4)
2375 {
2376 n = (((digest[i] << 8) | digest[i + 6]) << 8);
2377
2378 if (i < 4)
2379 n |= digest[i + 12];
2380 else
2381 n |= digest[5];
2382
2383 to64(ptr, n, 4);
2384 }
2385
2386 to64(ptr, digest[11], 2);
2387 ptr += 2;
2388 *ptr = '\0';
2389
2390 return (result);
2391 }
2392 else
2393 {
2394 /*
2395 * Use the standard crypt() function...
2396 */
2397
2398 return (crypt(pw, salt));
2399 }
2400}
d09495fa 2401#endif /* !HAVE_LIBPAM && !HAVE_USERSEC_H */
ef416fc2 2402
2403
10d09e33
MS
2404/*
2405 * 'free_authmask()' - Free function for auth masks.
2406 */
2407
2408static void
2409free_authmask(cupsd_authmask_t *mask, /* I - Auth mask to free */
2410 void *data) /* I - User data (unused) */
2411{
2412 (void)data;
2413
2414 if (mask->type == CUPSD_AUTH_NAME || mask->type == CUPSD_AUTH_INTERFACE)
2415 _cupsStrFree(mask->mask.name.name);
2416
2417 free(mask);
2418}
2419
2420
e1d6a774 2421/*
2422 * 'get_md5_password()' - Get an MD5 password.
2423 */
2424
2425static char * /* O - MD5 password string */
2426get_md5_password(const char *username, /* I - Username */
2427 const char *group, /* I - Group */
2428 char passwd[33]) /* O - MD5 password string */
2429{
2430 cups_file_t *fp; /* passwd.md5 file */
2431 char filename[1024], /* passwd.md5 filename */
2432 line[256], /* Line from file */
2433 tempuser[33], /* User from file */
2434 tempgroup[33]; /* Group from file */
2435
2436
2437 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2438 "get_md5_password(username=\"%s\", group=\"%s\", passwd=%p)",
2439 username, group ? group : "(null)", passwd);
2440
2441 snprintf(filename, sizeof(filename), "%s/passwd.md5", ServerRoot);
2442 if ((fp = cupsFileOpen(filename, "r")) == NULL)
2443 {
2444 if (errno != ENOENT)
2445 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to open %s - %s", filename,
2446 strerror(errno));
2447
2448 return (NULL);
2449 }
2450
2451 while (cupsFileGets(fp, line, sizeof(line)) != NULL)
2452 {
2453 if (sscanf(line, "%32[^:]:%32[^:]:%32s", tempuser, tempgroup, passwd) != 3)
2454 {
2455 cupsdLogMessage(CUPSD_LOG_ERROR, "Bad MD5 password line: %s", line);
2456 continue;
2457 }
2458
2459 if (!strcmp(username, tempuser) &&
2460 (group == NULL || !strcmp(group, tempgroup)))
2461 {
2462 /*
2463 * Found the password entry!
2464 */
2465
2466 cupsdLogMessage(CUPSD_LOG_DEBUG2, "Found MD5 user %s, group %s...",
2467 username, tempgroup);
2468
2469 cupsFileClose(fp);
2470 return (passwd);
2471 }
2472 }
2473
2474 /*
2475 * Didn't find a password entry - return NULL!
2476 */
2477
2478 cupsFileClose(fp);
2479 return (NULL);
2480}
2481
2482
ef416fc2 2483#if HAVE_LIBPAM
2484/*
2485 * 'pam_func()' - PAM conversation function.
2486 */
2487
2488static int /* O - Success or failure */
2489pam_func(
2490 int num_msg, /* I - Number of messages */
2491 const struct pam_message **msg, /* I - Messages */
2492 struct pam_response **resp, /* O - Responses */
2493 void *appdata_ptr)
2494 /* I - Pointer to connection */
2495{
2496 int i; /* Looping var */
2497 struct pam_response *replies; /* Replies */
2498 cupsd_authdata_t *data; /* Pointer to auth data */
2499
2500
2501 /*
2502 * Allocate memory for the responses...
2503 */
2504
2505 if ((replies = malloc(sizeof(struct pam_response) * num_msg)) == NULL)
2506 return (PAM_CONV_ERR);
2507
2508 /*
2509 * Answer all of the messages...
2510 */
2511
2512 DEBUG_printf(("pam_func: appdata_ptr = %p\n", appdata_ptr));
2513
2514#ifdef __hpux
2515 /*
2516 * Apparently some versions of HP-UX 11 have a broken pam_unix security
2517 * module. This is a workaround...
2518 */
2519
2520 data = auth_data;
2521 (void)appdata_ptr;
2522#else
2523 data = (cupsd_authdata_t *)appdata_ptr;
2524#endif /* __hpux */
2525
2526 for (i = 0; i < num_msg; i ++)
2527 {
2528 DEBUG_printf(("pam_func: Message = \"%s\"\n", msg[i]->msg));
2529
2530 switch (msg[i]->msg_style)
2531 {
2532 case PAM_PROMPT_ECHO_ON:
2533 DEBUG_printf(("pam_func: PAM_PROMPT_ECHO_ON, returning \"%s\"...\n",
2534 data->username));
2535 replies[i].resp_retcode = PAM_SUCCESS;
2536 replies[i].resp = strdup(data->username);
2537 break;
2538
2539 case PAM_PROMPT_ECHO_OFF:
2540 DEBUG_printf(("pam_func: PAM_PROMPT_ECHO_OFF, returning \"%s\"...\n",
2541 data->password));
2542 replies[i].resp_retcode = PAM_SUCCESS;
2543 replies[i].resp = strdup(data->password);
2544 break;
2545
2546 case PAM_TEXT_INFO:
2547 DEBUG_puts("pam_func: PAM_TEXT_INFO...");
2548 replies[i].resp_retcode = PAM_SUCCESS;
2549 replies[i].resp = NULL;
2550 break;
2551
2552 case PAM_ERROR_MSG:
2553 DEBUG_puts("pam_func: PAM_ERROR_MSG...");
2554 replies[i].resp_retcode = PAM_SUCCESS;
2555 replies[i].resp = NULL;
2556 break;
2557
2558 default:
2559 DEBUG_printf(("pam_func: Unknown PAM message %d...\n",
2560 msg[i]->msg_style));
2561 free(replies);
2562 return (PAM_CONV_ERR);
2563 }
2564 }
2565
2566 /*
2567 * Return the responses back to PAM...
2568 */
2569
2570 *resp = replies;
2571
2572 return (PAM_SUCCESS);
2573}
f7deaa1a 2574#elif !defined(HAVE_USERSEC_H)
ef416fc2 2575
2576
2577/*
2578 * 'to64()' - Base64-encode an integer value...
2579 */
2580
2581static void
2582to64(char *s, /* O - Output string */
2583 unsigned long v, /* I - Value to encode */
2584 int n) /* I - Number of digits */
2585{
2586 const char *itoa64 = "./0123456789"
2587 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2588 "abcdefghijklmnopqrstuvwxyz";
2589
2590
2591 for (; n > 0; n --, v >>= 6)
2592 *s++ = itoa64[v & 0x3f];
2593}
2594#endif /* HAVE_LIBPAM */
2595
2596
2597/*
b19ccc9e 2598 * End of "$Id: auth.c 7830 2008-08-04 20:38:50Z mike $".
ef416fc2 2599 */