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