]>
Commit | Line | Data |
---|---|---|
0e66904d | 1 | ;/* |
b2e10895 | 2 | * "$Id$" |
824bac0b | 3 | * |
fd8b1cf8 | 4 | * Authorization routines for the Common UNIX Printing System (CUPS). |
824bac0b | 5 | * |
0e66904d | 6 | * Copyright 1997-2006 by Easy Software Products, all rights reserved. |
824bac0b | 7 | * |
8 | * These coded instructions, statements, and computer programs are the | |
9 | * property of Easy Software Products and are protected by Federal | |
10 | * copyright law. Distribution and use rights are outlined in the file | |
11 | * "LICENSE.txt" which should have been included with this file. If this | |
12 | * file is missing or damaged please contact Easy Software Products | |
13 | * at: | |
14 | * | |
15 | * Attn: CUPS Licensing Information | |
16 | * Easy Software Products | |
8784b6a6 | 17 | * 44141 Airport View Drive, Suite 204 |
8650fcf2 | 18 | * Hollywood, Maryland 20636 USA |
824bac0b | 19 | * |
edfd3c3d | 20 | * Voice: (301) 373-9600 |
824bac0b | 21 | * EMail: cups-info@cups.org |
22 | * WWW: http://www.cups.org | |
23 | * | |
24 | * Contents: | |
25 | * | |
f3e786fc | 26 | * cupsdAddLocation() - Add a location for authorization. |
27 | * cupsdAddName() - Add a name to a location... | |
28 | * cupsdAllowHost() - Add a host name that is allowed to access the | |
29 | * location. | |
30 | * cupsdAllowIP() - Add an IP address or network that is allowed to | |
31 | * access the location. | |
32 | * cupsdCheckAuth() - Check authorization masks. | |
33 | * cupsdCheckGroup() - Check for a user's group membership. | |
34 | * cupsdCopyLocation() - Make a copy of a location... | |
35 | * cupsdDeleteAllLocations() - Free all memory used for location authorization. | |
36 | * cupsdDeleteLocation() - Free all memory used by a location. | |
37 | * cupsdDenyHost() - Add a host name that is not allowed to access the | |
38 | * location. | |
39 | * cupsdDenyIP() - Add an IP address or network that is not allowed | |
40 | * to access the location. | |
41 | * cupsdFindBest() - Find the location entry that best matches the | |
42 | * resource. | |
43 | * cupsdFindLocation() - Find the named location. | |
44 | * cupsdGetMD5Passwd() - Get an MD5 password. | |
45 | * cupsdIsAuthorized() - Check to see if the user is authorized... | |
46 | * add_allow() - Add an allow mask to the location. | |
47 | * add_deny() - Add a deny mask to the location. | |
ad4b44a5 | 48 | * compare_locations() - Compare two locations. |
f3e786fc | 49 | * cups_crypt() - Encrypt the password using the DES or MD5 |
50 | * algorithms, as needed. | |
51 | * pam_func() - PAM conversation function. | |
52 | * to64() - Base64-encode an integer value... | |
824bac0b | 53 | */ |
54 | ||
55 | /* | |
56 | * Include necessary headers... | |
57 | */ | |
58 | ||
fd8b1cf8 | 59 | #include "cupsd.h" |
fd8b1cf8 | 60 | #include <grp.h> |
61 | #ifdef HAVE_SHADOW_H | |
62 | # include <shadow.h> | |
63 | #endif /* HAVE_SHADOW_H */ | |
ba8a42d9 | 64 | #ifdef HAVE_CRYPT_H |
65 | # include <crypt.h> | |
66 | #endif /* HAVE_CRYPT_H */ | |
a6239644 | 67 | #if HAVE_LIBPAM |
ed6078a4 | 68 | # ifdef HAVE_PAM_PAM_APPL_H |
69 | # include <pam/pam_appl.h> | |
70 | # else | |
71 | # include <security/pam_appl.h> | |
72 | # endif /* HAVE_PAM_PAM_APPL_H */ | |
664de97a | 73 | #endif /* HAVE_LIBPAM */ |
b5cb0608 | 74 | #ifdef HAVE_USERSEC_H |
75 | # include <usersec.h> | |
76 | #endif /* HAVE_USERSEC_H */ | |
664de97a | 77 | |
fd8b1cf8 | 78 | |
79 | /* | |
80 | * Local functions... | |
81 | */ | |
82 | ||
589eb420 | 83 | static cupsd_authmask_t *add_allow(cupsd_location_t *loc); |
84 | static cupsd_authmask_t *add_deny(cupsd_location_t *loc); | |
ad4b44a5 | 85 | static int compare_locations(cupsd_location_t *a, |
86 | cupsd_location_t *b); | |
753453e4 | 87 | #if !HAVE_LIBPAM |
88 | static char *cups_crypt(const char *pw, const char *salt); | |
89 | #endif /* !HAVE_LIBPAM */ | |
a6239644 | 90 | #if HAVE_LIBPAM |
664de97a | 91 | static int pam_func(int, const struct pam_message **, |
92 | struct pam_response **, void *); | |
753453e4 | 93 | #else |
94 | static void to64(char *s, unsigned long v, int n); | |
664de97a | 95 | #endif /* HAVE_LIBPAM */ |
fd8b1cf8 | 96 | |
97 | ||
ff84728d | 98 | /* |
99 | * Local structures... | |
100 | */ | |
101 | ||
102 | #if HAVE_LIBPAM | |
103 | typedef struct cupsd_authdata_s /**** Authentication data ****/ | |
104 | { | |
105 | char username[33], /* Username string */ | |
106 | password[33]; /* Password string */ | |
107 | } cupsd_authdata_t; | |
108 | #endif /* HAVE_LIBPAM */ | |
109 | ||
110 | ||
b5cb0608 | 111 | /* |
112 | * Local globals... | |
113 | */ | |
114 | ||
adf75dba | 115 | #if defined(__hpux) && defined(HAVE_LIBPAM) |
ff84728d | 116 | static cupsd_authdata_t *auth_datat; /* Current client being authenticated */ |
adf75dba | 117 | #endif /* __hpux && HAVE_LIBPAM */ |
b5cb0608 | 118 | |
119 | ||
fd8b1cf8 | 120 | /* |
589eb420 | 121 | * 'cupsdAddLocation()' - Add a location for authorization. |
fd8b1cf8 | 122 | */ |
123 | ||
f3e786fc | 124 | cupsd_location_t * /* O - Pointer to new location record */ |
589eb420 | 125 | cupsdAddLocation(const char *location) /* I - Location path */ |
fd8b1cf8 | 126 | { |
f3e786fc | 127 | cupsd_location_t *temp; /* New location */ |
fd8b1cf8 | 128 | |
129 | ||
130 | /* | |
ad4b44a5 | 131 | * Make sure the locations array is created... |
fd8b1cf8 | 132 | */ |
133 | ||
ad4b44a5 | 134 | if (!Locations) |
135 | Locations = cupsArrayNew((cups_array_func_t)compare_locations, NULL); | |
fd8b1cf8 | 136 | |
ad4b44a5 | 137 | if (!Locations) |
fd8b1cf8 | 138 | return (NULL); |
139 | ||
ad4b44a5 | 140 | /* |
141 | * Try to allocate memory for the new location. | |
142 | */ | |
143 | ||
144 | if ((temp = calloc(1, sizeof(cupsd_location_t))) == NULL) | |
145 | return (NULL); | |
fd8b1cf8 | 146 | |
147 | /* | |
148 | * Initialize the record and copy the name over... | |
149 | */ | |
150 | ||
ad4b44a5 | 151 | temp->location = strdup(location); |
152 | temp->length = strlen(temp->location); | |
153 | ||
154 | cupsArrayAdd(Locations, temp); | |
fd8b1cf8 | 155 | |
f3e786fc | 156 | cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdAddLocation: added location \'%s\'", |
157 | location); | |
fd8b1cf8 | 158 | |
159 | /* | |
160 | * Return the new record... | |
161 | */ | |
162 | ||
163 | return (temp); | |
164 | } | |
165 | ||
166 | ||
46490d9d | 167 | /* |
589eb420 | 168 | * 'cupsdAddName()' - Add a name to a location... |
46490d9d | 169 | */ |
170 | ||
171 | void | |
589eb420 | 172 | cupsdAddName(cupsd_location_t *loc, /* I - Location to add to */ |
f3e786fc | 173 | char *name) /* I - Name to add */ |
46490d9d | 174 | { |
f3e786fc | 175 | char **temp; /* Pointer to names array */ |
46490d9d | 176 | |
177 | ||
f3e786fc | 178 | cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdAddName(loc=%p, name=\"%s\")", |
179 | loc, name); | |
a8c7842b | 180 | |
46490d9d | 181 | if (loc->num_names == 0) |
182 | temp = malloc(sizeof(char *)); | |
183 | else | |
184 | temp = realloc(loc->names, (loc->num_names + 1) * sizeof(char *)); | |
185 | ||
186 | if (temp == NULL) | |
187 | { | |
f3e786fc | 188 | cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to add name to location %s: %s", |
189 | loc->location, strerror(errno)); | |
46490d9d | 190 | return; |
191 | } | |
192 | ||
193 | loc->names = temp; | |
194 | ||
195 | if ((temp[loc->num_names] = strdup(name)) == NULL) | |
196 | { | |
f3e786fc | 197 | cupsdLogMessage(CUPSD_LOG_ERROR, |
198 | "Unable to duplicate name for location %s: %s", | |
199 | loc->location, strerror(errno)); | |
46490d9d | 200 | return; |
201 | } | |
202 | ||
203 | loc->num_names ++; | |
204 | } | |
205 | ||
206 | ||
fd8b1cf8 | 207 | /* |
589eb420 | 208 | * 'cupsdAllowHost()' - Add a host name that is allowed to access the location. |
fd8b1cf8 | 209 | */ |
210 | ||
211 | void | |
589eb420 | 212 | cupsdAllowHost(cupsd_location_t *loc, /* I - Location to add to */ |
f3e786fc | 213 | char *name) /* I - Name of host or domain to add */ |
fd8b1cf8 | 214 | { |
589eb420 | 215 | cupsd_authmask_t *temp; /* New host/domain mask */ |
f3e786fc | 216 | char ifname[32], /* Interface name */ |
217 | *ifptr; /* Pointer to end of name */ | |
fd8b1cf8 | 218 | |
219 | ||
f3e786fc | 220 | cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdAllowHost(loc=%p(%s), name=\"%s\")", |
221 | loc, loc->location, name); | |
a8c7842b | 222 | |
fd8b1cf8 | 223 | if ((temp = add_allow(loc)) == NULL) |
224 | return; | |
225 | ||
ad4b44a5 | 226 | if (!strcasecmp(name, "@LOCAL")) |
20264b99 | 227 | { |
228 | /* | |
229 | * Allow *interface*... | |
230 | */ | |
231 | ||
232 | temp->type = AUTH_INTERFACE; | |
cadf73c6 | 233 | temp->mask.name.name = strdup("*"); |
20264b99 | 234 | temp->mask.name.length = 1; |
235 | } | |
ad4b44a5 | 236 | else if (!strncasecmp(name, "@IF(", 4)) |
20264b99 | 237 | { |
238 | /* | |
239 | * Allow *interface*... | |
240 | */ | |
241 | ||
def978d5 | 242 | strlcpy(ifname, name + 4, sizeof(ifname)); |
20264b99 | 243 | |
244 | ifptr = ifname + strlen(ifname); | |
245 | ||
246 | if (ifptr[-1] == ')') | |
247 | { | |
248 | ifptr --; | |
249 | *ifptr = '\0'; | |
250 | } | |
251 | ||
252 | temp->type = AUTH_INTERFACE; | |
253 | temp->mask.name.name = strdup(ifname); | |
254 | temp->mask.name.length = ifptr - ifname; | |
255 | } | |
256 | else | |
257 | { | |
258 | /* | |
259 | * Allow name... | |
260 | */ | |
261 | ||
262 | temp->type = AUTH_NAME; | |
263 | temp->mask.name.name = strdup(name); | |
264 | temp->mask.name.length = strlen(name); | |
265 | } | |
fd8b1cf8 | 266 | } |
267 | ||
268 | ||
269 | /* | |
f3e786fc | 270 | * 'cupsdAllowIP()' - Add an IP address or network that is allowed to access |
271 | * the location. | |
fd8b1cf8 | 272 | */ |
273 | ||
274 | void | |
589eb420 | 275 | cupsdAllowIP(cupsd_location_t *loc, /* I - Location to add to */ |
f3e786fc | 276 | unsigned address[4], /* I - IP address to add */ |
277 | unsigned netmask[4]) /* I - Netmask of address */ | |
fd8b1cf8 | 278 | { |
589eb420 | 279 | cupsd_authmask_t *temp; /* New host/domain mask */ |
fd8b1cf8 | 280 | |
281 | ||
f3e786fc | 282 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
283 | "cupsdAllowIP(loc=%p(%s), address=%x:%x:%x:%x, netmask=%x:%x:%x:%x)", | |
284 | loc, loc->location, address[0], address[1], address[2], | |
285 | address[3], netmask[0], netmask[1], netmask[2], | |
286 | netmask[3]); | |
a8c7842b | 287 | |
fd8b1cf8 | 288 | if ((temp = add_allow(loc)) == NULL) |
289 | return; | |
290 | ||
99de6da0 | 291 | temp->type = AUTH_IP; |
a8c7842b | 292 | memcpy(temp->mask.ip.address, address, sizeof(temp->mask.ip.address)); |
293 | memcpy(temp->mask.ip.netmask, netmask, sizeof(temp->mask.ip.netmask)); | |
fd8b1cf8 | 294 | } |
295 | ||
296 | ||
ff84728d | 297 | /* |
298 | * 'cupsdAuthorize()' - Validate any authorization credentials. | |
299 | */ | |
300 | ||
301 | void | |
302 | cupsdAuthorize(cupsd_client_t *con) /* I - Client connection */ | |
303 | { | |
304 | int type; /* Authentication type */ | |
305 | char *authorization, /* Pointer into Authorization string */ | |
306 | *ptr, /* Pointer into string */ | |
307 | username[65], /* Username string */ | |
308 | password[33]; /* Password string */ | |
309 | const char *localuser; /* Certificate username */ | |
310 | char nonce[HTTP_MAX_VALUE], /* Nonce value from client */ | |
311 | md5[33], /* MD5 password */ | |
312 | basicmd5[33]; /* MD5 of Basic password */ | |
313 | static const char * const states[] = /* HTTP client states... */ | |
314 | { | |
315 | "WAITING", | |
316 | "OPTIONS", | |
317 | "GET", | |
318 | "GET", | |
319 | "HEAD", | |
320 | "POST", | |
321 | "POST", | |
322 | "POST", | |
323 | "PUT", | |
324 | "PUT", | |
325 | "DELETE", | |
326 | "TRACE", | |
327 | "CLOSE", | |
328 | "STATUS" | |
329 | }; | |
330 | ||
331 | ||
332 | /* | |
333 | * Locate the best matching location so we know what kind of | |
334 | * authentication to expect... | |
335 | */ | |
336 | ||
337 | con->best = cupsdFindBest(con->uri, con->http.state); | |
338 | ||
339 | cupsdLogMessage(CUPSD_LOG_DEBUG2, | |
340 | "cupsdAuthorize: con->uri=\"%s\", con->best=%p(%s)", | |
341 | con->uri, con->best, con->best ? con->best->location : ""); | |
342 | ||
343 | if (con->best && con->best->type != AUTH_NONE) | |
344 | type = con->best->type; | |
345 | else | |
346 | type = DefaultAuthType; | |
347 | ||
348 | /* | |
349 | * Decode the Authorization string... | |
350 | */ | |
351 | ||
352 | authorization = con->http.fields[HTTP_FIELD_AUTHORIZATION]; | |
353 | ||
354 | cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdAuthorize: Authorization=\"%s\"", | |
355 | authorization); | |
356 | ||
357 | username[0] = '\0'; | |
358 | password[0] = '\0'; | |
359 | ||
360 | if (type == AUTH_NONE) | |
361 | { | |
362 | /* | |
363 | * No authorization required, return early... | |
364 | */ | |
365 | ||
366 | cupsdLogMessage(CUPSD_LOG_DEBUG, | |
367 | "cupsdAuthorize: No authentication required."); | |
368 | return; | |
369 | } | |
370 | else if (!*authorization) | |
371 | { | |
372 | /* | |
373 | * No authorization data provided, return early... | |
374 | */ | |
375 | ||
376 | cupsdLogMessage(CUPSD_LOG_DEBUG, | |
377 | "cupsdAuthorize: No authentication data provided."); | |
378 | return; | |
379 | } | |
380 | else if (!strncmp(authorization, "Local", 5) && | |
381 | !strcasecmp(con->http.hostname, "localhost")) | |
382 | { | |
383 | /* | |
384 | * Get Local certificate authentication data... | |
385 | */ | |
386 | ||
387 | authorization += 5; | |
388 | while (isspace(*authorization)) | |
389 | authorization ++; | |
390 | ||
391 | if ((localuser = cupsdFindCert(authorization)) != NULL) | |
392 | strlcpy(username, localuser, sizeof(username)); | |
393 | else | |
394 | { | |
395 | cupsdLogMessage(CUPSD_LOG_ERROR, | |
396 | "cupsdAuthorize: Local authentication certificate not " | |
397 | "found!"); | |
398 | return; | |
399 | } | |
400 | } | |
401 | else if (!strncmp(authorization, "Basic", 5) && | |
402 | (type == AUTH_BASIC || type == AUTH_BASICDIGEST)) | |
403 | { | |
404 | /* | |
405 | * Get the Basic authentication data... | |
406 | */ | |
407 | ||
0e66904d | 408 | int userlen; /* Username:password length */ |
409 | ||
410 | ||
ff84728d | 411 | authorization += 5; |
412 | while (isspace(*authorization)) | |
413 | authorization ++; | |
414 | ||
0e66904d | 415 | userlen = sizeof(username); |
416 | httpDecode64_2(username, &userlen, authorization); | |
ff84728d | 417 | |
418 | /* | |
419 | * Pull the username and password out... | |
420 | */ | |
421 | ||
422 | if ((ptr = strchr(username, ':')) == NULL) | |
423 | { | |
424 | cupsdLogMessage(CUPSD_LOG_ERROR, | |
425 | "cupsdAuthorize: Missing Basic password!"); | |
426 | return; | |
427 | } | |
428 | ||
429 | *ptr++ = '\0'; | |
430 | ||
431 | if (!username[0]) | |
432 | { | |
433 | /* | |
434 | * Username must not be empty... | |
435 | */ | |
436 | ||
437 | cupsdLogMessage(CUPSD_LOG_ERROR, | |
438 | "cupsdAuthorize: Empty Basic username!"); | |
439 | return; | |
440 | } | |
441 | ||
442 | if (!*ptr) | |
443 | { | |
444 | /* | |
445 | * Password must not be empty... | |
446 | */ | |
447 | ||
448 | cupsdLogMessage(CUPSD_LOG_ERROR, | |
449 | "cupsdAuthorize: Empty Basic password!"); | |
450 | return; | |
451 | } | |
452 | ||
453 | strlcpy(password, ptr, sizeof(password)); | |
454 | ||
455 | /* | |
456 | * Validate the username and password... | |
457 | */ | |
458 | ||
459 | switch (type) | |
460 | { | |
461 | case AUTH_BASIC : | |
462 | { | |
463 | #if HAVE_LIBPAM | |
464 | /* | |
465 | * Only use PAM to do authentication. This supports MD5 | |
466 | * passwords, among other things... | |
467 | */ | |
468 | ||
469 | pam_handle_t *pamh; /* PAM authentication handle */ | |
470 | int pamerr; /* PAM error code */ | |
471 | struct pam_conv pamdata;/* PAM conversation data */ | |
472 | cupsd_authdata_t data; /* Authentication data */ | |
473 | ||
474 | ||
475 | strlcpy(data.username, username, sizeof(data.username)); | |
476 | strlcpy(data.password, password, sizeof(data.password)); | |
477 | ||
478 | pamdata.conv = pam_func; | |
479 | pamdata.appdata_ptr = &data; | |
480 | ||
481 | # ifdef __hpux | |
482 | /* | |
483 | * Workaround for HP-UX bug in pam_unix; see pam_func() below for | |
484 | * more info... | |
485 | */ | |
486 | ||
487 | auth_data = &data; | |
488 | # endif /* __hpux */ | |
489 | ||
490 | pamerr = pam_start("cups", username, &pamdata, &pamh); | |
491 | if (pamerr != PAM_SUCCESS) | |
492 | { | |
493 | cupsdLogMessage(CUPSD_LOG_ERROR, | |
494 | "cupsdAuthorize: pam_start() returned %d (%s)!\n", | |
495 | pamerr, pam_strerror(pamh, pamerr)); | |
496 | pam_end(pamh, 0); | |
497 | return; | |
498 | } | |
499 | ||
500 | pamerr = pam_authenticate(pamh, PAM_SILENT); | |
501 | if (pamerr != PAM_SUCCESS) | |
502 | { | |
503 | cupsdLogMessage(CUPSD_LOG_ERROR, | |
504 | "cupsdAuthorize: pam_authenticate() returned %d " | |
505 | "(%s)!\n", | |
506 | pamerr, pam_strerror(pamh, pamerr)); | |
507 | pam_end(pamh, 0); | |
508 | return; | |
509 | } | |
510 | ||
511 | pamerr = pam_acct_mgmt(pamh, PAM_SILENT); | |
512 | if (pamerr != PAM_SUCCESS) | |
513 | { | |
514 | cupsdLogMessage(CUPSD_LOG_ERROR, | |
515 | "cupsdAuthorize: pam_acct_mgmt() returned %d " | |
516 | "(%s)!\n", | |
517 | pamerr, pam_strerror(pamh, pamerr)); | |
518 | pam_end(pamh, 0); | |
519 | return; | |
520 | } | |
521 | ||
522 | pam_end(pamh, PAM_SUCCESS); | |
523 | ||
524 | #elif defined(HAVE_USERSEC_H) | |
525 | /* | |
526 | * Use AIX authentication interface... | |
527 | */ | |
528 | ||
529 | char *authmsg; /* Authentication message */ | |
530 | char *loginmsg; /* Login message */ | |
531 | int reenter; /* ??? */ | |
532 | ||
533 | ||
534 | cupsdLogMessage(CUPSD_LOG_DEBUG, | |
535 | "cupsdAuthorize: AIX authenticate of username \"%s\"", | |
536 | username); | |
537 | ||
538 | reenter = 1; | |
539 | if (authenticate(username, password, &reenter, &authmsg) != 0) | |
540 | { | |
541 | cupsdLogMessage(CUPSD_LOG_DEBUG, | |
542 | "cupsdAuthorize: Unable to authenticate username " | |
543 | "\"%s\": %s", | |
544 | username, strerror(errno)); | |
545 | return; | |
546 | } | |
547 | ||
548 | #else | |
549 | /* | |
550 | * Use normal UNIX password file-based authentication... | |
551 | */ | |
552 | ||
553 | char *pass; /* Encrypted password */ | |
554 | struct passwd *pw; /* User password data */ | |
555 | # ifdef HAVE_SHADOW_H | |
556 | struct spwd *spw; /* Shadow password data */ | |
557 | # endif /* HAVE_SHADOW_H */ | |
558 | ||
559 | ||
560 | pw = getpwnam(username); /* Get the current password */ | |
561 | endpwent(); /* Close the password file */ | |
562 | ||
563 | if (!pw) | |
564 | { | |
565 | /* | |
566 | * No such user... | |
567 | */ | |
568 | ||
569 | cupsdLogMessage(CUPSD_LOG_ERROR, | |
570 | "cupsdAuthorize: Unknown username \"%s\"!", | |
571 | username); | |
572 | return (HTTP_UNAUTHORIZED); | |
573 | } | |
574 | ||
575 | # ifdef HAVE_SHADOW_H | |
576 | spw = getspnam(username); | |
577 | endspent(); | |
578 | ||
579 | if (!spw && !strcmp(pw->pw_passwd, "x")) | |
580 | { | |
581 | /* | |
582 | * Don't allow blank passwords! | |
583 | */ | |
584 | ||
585 | cupsdLogMessage(CUPSD_LOG_ERROR, | |
586 | "cupsdAuthorize: Username \"%s\" has no shadow " | |
587 | "password!", username); | |
588 | return; | |
589 | } | |
590 | ||
591 | if (spw && !spw->sp_pwdp[0] && !pw->pw_passwd[0]) | |
592 | # else | |
593 | if (!pw->pw_passwd[0]) | |
594 | # endif /* HAVE_SHADOW_H */ | |
595 | { | |
596 | /* | |
597 | * Don't allow blank passwords! | |
598 | */ | |
599 | ||
600 | cupsdLogMessage(CUPSD_LOG_ERROR, | |
601 | "cupsdAuthorize: Username \"%s\" has no password!", | |
602 | username); | |
603 | return; | |
604 | } | |
605 | ||
606 | /* | |
607 | * OK, the password isn't blank, so compare with what came from the | |
608 | * client... | |
609 | */ | |
610 | ||
611 | pass = cups_crypt(password, pw->pw_passwd); | |
612 | ||
613 | cupsdLogMessage(CUPSD_LOG_DEBUG2, | |
614 | "cupsdAuthorize: pw_passwd=\"%s\", crypt=\"%s\"", | |
615 | pw->pw_passwd, pass); | |
616 | ||
617 | if (!pass || strcmp(pw->pw_passwd, pass)) | |
618 | { | |
619 | # ifdef HAVE_SHADOW_H | |
620 | if (spw) | |
621 | { | |
622 | pass = cups_crypt(password, spw->sp_pwdp); | |
623 | ||
624 | cupsdLogMessage(CUPSD_LOG_DEBUG2, | |
625 | "cupsdAuthorize: sp_pwdp=\"%s\", crypt=\"%s\"", | |
626 | spw->sp_pwdp, pass); | |
627 | ||
628 | if (pass == NULL || strcmp(spw->sp_pwdp, pass)) | |
629 | { | |
630 | cupsdLogMessage(CUPSD_LOG_ERROR, | |
631 | "cupsdAuthorize: Authentication failed for " | |
632 | "user \"%s\"!", | |
633 | username); | |
634 | return; | |
635 | } | |
636 | } | |
637 | else | |
638 | # endif /* HAVE_SHADOW_H */ | |
639 | { | |
640 | cupsdLogMessage(CUPSD_LOG_ERROR, | |
641 | "cupsdAuthorize: Authentication failed for " | |
642 | "user \"%s\"!", | |
643 | username); | |
644 | return; | |
645 | } | |
646 | } | |
647 | #endif /* HAVE_LIBPAM */ | |
648 | } | |
649 | break; | |
650 | ||
651 | case AUTH_BASICDIGEST : | |
652 | /* | |
653 | * Do Basic authentication with the Digest password file... | |
654 | */ | |
655 | ||
656 | if (!cupsdGetMD5Passwd(username, NULL, md5)) | |
657 | { | |
658 | cupsdLogMessage(CUPSD_LOG_ERROR, | |
659 | "cupsdAuthorize: Unknown MD5 username \"%s\"!", | |
660 | username); | |
661 | return; | |
662 | } | |
663 | ||
664 | httpMD5(username, "CUPS", password, basicmd5); | |
665 | ||
666 | if (strcmp(md5, basicmd5)) | |
667 | { | |
668 | cupsdLogMessage(CUPSD_LOG_ERROR, | |
669 | "cupsdAuthorize: Authentication failed for \"%s\"!", | |
670 | username); | |
671 | return; | |
672 | } | |
673 | break; | |
674 | } | |
675 | } | |
676 | else if (!strncmp(authorization, "Digest", 6) && type == AUTH_DIGEST) | |
677 | { | |
678 | /* | |
679 | * Get the username, password, and nonce from the Digest attributes... | |
680 | */ | |
681 | ||
682 | if (!httpGetSubField2(&(con->http), HTTP_FIELD_AUTHORIZATION, "username", | |
683 | username, sizeof(username)) || !username[0]) | |
684 | { | |
685 | /* | |
686 | * Username must not be empty... | |
687 | */ | |
688 | ||
689 | cupsdLogMessage(CUPSD_LOG_ERROR, | |
690 | "cupsdAuthorize: Empty or missing Digest username!"); | |
691 | return; | |
692 | } | |
693 | ||
694 | if (!httpGetSubField2(&(con->http), HTTP_FIELD_AUTHORIZATION, "response", | |
695 | password, sizeof(password)) || !password[0]) | |
696 | { | |
697 | /* | |
698 | * Password must not be empty... | |
699 | */ | |
700 | ||
701 | cupsdLogMessage(CUPSD_LOG_ERROR, | |
702 | "cupsdAuthorize: Empty or missing Digest password!"); | |
703 | return; | |
704 | } | |
705 | ||
706 | if (!httpGetSubField(&(con->http), HTTP_FIELD_AUTHORIZATION, "nonce", | |
707 | nonce)) | |
708 | { | |
709 | cupsdLogMessage(CUPSD_LOG_ERROR, | |
710 | "cupsdAuthorize: No nonce value for Digest " | |
711 | "authentication!"); | |
712 | return; | |
713 | } | |
714 | ||
715 | if (strcmp(con->http.hostname, nonce)) | |
716 | { | |
717 | cupsdLogMessage(CUPSD_LOG_ERROR, | |
718 | "cupsdAuthorize: Bad nonce value, expected \"%s\", " | |
719 | "got \"%s\"!", con->http.hostname, nonce); | |
720 | return; | |
721 | } | |
722 | ||
723 | /* | |
724 | * Validate the username and password... | |
725 | */ | |
726 | ||
727 | if (!cupsdGetMD5Passwd(username, NULL, md5)) | |
728 | { | |
729 | cupsdLogMessage(CUPSD_LOG_ERROR, | |
730 | "cupsdAuthorize: Unknown MD5 username \"%s\"!", | |
731 | username); | |
732 | return; | |
733 | } | |
734 | ||
735 | httpMD5Final(nonce, states[con->http.state], con->uri, md5); | |
736 | ||
737 | if (strcmp(md5, password)) | |
738 | { | |
739 | cupsdLogMessage(CUPSD_LOG_ERROR, | |
740 | "cupsdAuthorize: Authentication failed for \"%s\"!", | |
741 | username); | |
742 | return; | |
743 | } | |
744 | } | |
745 | else | |
746 | { | |
747 | cupsdLogMessage(CUPSD_LOG_DEBUG, | |
748 | "cupsdAuthorize: Bad authentication data."); | |
749 | return; | |
750 | } | |
751 | ||
752 | /* | |
753 | * If we get here, then we were able to validate the username and | |
754 | * password - copy the validated username and password to the client | |
755 | * data and return... | |
756 | */ | |
757 | ||
758 | strlcpy(con->username, username, sizeof(con->username)); | |
759 | strlcpy(con->password, password, sizeof(con->password)); | |
760 | ||
761 | cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdAuthorize: username=\"%s\"", | |
762 | con->username); | |
763 | } | |
764 | ||
765 | ||
e5ebb675 | 766 | /* |
589eb420 | 767 | * 'cupsdCheckAuth()' - Check authorization masks. |
e5ebb675 | 768 | */ |
769 | ||
f3e786fc | 770 | int /* O - 1 if mask matches, 0 otherwise */ |
771 | cupsdCheckAuth( | |
772 | unsigned ip[4], /* I - Client address */ | |
773 | char *name, /* I - Client hostname */ | |
774 | int name_len, /* I - Length of hostname */ | |
775 | int num_masks, /* I - Number of masks */ | |
776 | cupsd_authmask_t *masks) /* I - Masks */ | |
e5ebb675 | 777 | { |
f3e786fc | 778 | int i; /* Looping var */ |
779 | cupsd_netif_t *iface; /* Network interface */ | |
780 | unsigned netip4; /* IPv4 network address */ | |
7c298ddc | 781 | #ifdef AF_INET6 |
f3e786fc | 782 | unsigned netip6[4]; /* IPv6 network address */ |
7c298ddc | 783 | #endif /* AF_INET6 */ |
99de6da0 | 784 | |
e5ebb675 | 785 | while (num_masks > 0) |
786 | { | |
787 | switch (masks->type) | |
788 | { | |
20264b99 | 789 | case AUTH_INTERFACE : |
790 | /* | |
791 | * Check for a match with a network interface... | |
792 | */ | |
793 | ||
7c298ddc | 794 | netip4 = htonl(ip[3]); |
795 | ||
796 | #ifdef AF_INET6 | |
20264b99 | 797 | netip6[0] = htonl(ip[0]); |
798 | netip6[1] = htonl(ip[1]); | |
799 | netip6[2] = htonl(ip[2]); | |
800 | netip6[3] = htonl(ip[3]); | |
7c298ddc | 801 | #endif /* AF_INET6 */ |
20264b99 | 802 | |
7c298ddc | 803 | if (!strcmp(masks->mask.name.name, "*")) |
20264b99 | 804 | { |
805 | /* | |
806 | * Check against all local interfaces... | |
807 | */ | |
808 | ||
589eb420 | 809 | cupsdNetIFUpdate(); |
20264b99 | 810 | |
71fe79e8 | 811 | for (iface = (cupsd_netif_t *)cupsArrayFirst(NetIFList); |
812 | iface; | |
813 | iface = (cupsd_netif_t *)cupsArrayNext(NetIFList)) | |
20264b99 | 814 | { |
815 | /* | |
816 | * Only check local interfaces... | |
817 | */ | |
818 | ||
819 | if (!iface->is_local) | |
820 | continue; | |
821 | ||
822 | if (iface->address.addr.sa_family == AF_INET) | |
823 | { | |
824 | /* | |
825 | * Check IPv4 address... | |
826 | */ | |
827 | ||
828 | if ((netip4 & iface->mask.ipv4.sin_addr.s_addr) == | |
829 | (iface->address.ipv4.sin_addr.s_addr & | |
830 | iface->mask.ipv4.sin_addr.s_addr)) | |
831 | return (1); | |
832 | } | |
a09840c5 | 833 | #ifdef AF_INET6 |
20264b99 | 834 | else |
835 | { | |
836 | /* | |
837 | * Check IPv6 address... | |
838 | */ | |
839 | ||
840 | for (i = 0; i < 4; i ++) | |
841 | if ((netip6[i] & iface->mask.ipv6.sin6_addr.s6_addr32[i]) != | |
842 | (iface->address.ipv6.sin6_addr.s6_addr32[i] & | |
843 | iface->mask.ipv6.sin6_addr.s6_addr32[i])) | |
844 | break; | |
845 | ||
846 | if (i == 4) | |
847 | return (1); | |
848 | } | |
a09840c5 | 849 | #endif /* AF_INET6 */ |
20264b99 | 850 | } |
851 | } | |
852 | else | |
853 | { | |
854 | /* | |
855 | * Check the named interface... | |
856 | */ | |
857 | ||
71fe79e8 | 858 | for (iface = (cupsd_netif_t *)cupsArrayFirst(NetIFList); |
859 | iface && !strcmp(masks->mask.name.name, iface->name); | |
860 | iface = (cupsd_netif_t *)cupsArrayNext(NetIFList)) | |
20264b99 | 861 | { |
cadf73c6 | 862 | if (iface->address.addr.sa_family == AF_INET) |
863 | { | |
864 | /* | |
865 | * Check IPv4 address... | |
866 | */ | |
20264b99 | 867 | |
cadf73c6 | 868 | if ((netip4 & iface->mask.ipv4.sin_addr.s_addr) == |
869 | (iface->address.ipv4.sin_addr.s_addr & | |
870 | iface->mask.ipv4.sin_addr.s_addr)) | |
871 | return (1); | |
872 | } | |
a09840c5 | 873 | #ifdef AF_INET6 |
cadf73c6 | 874 | else |
875 | { | |
876 | /* | |
877 | * Check IPv6 address... | |
878 | */ | |
20264b99 | 879 | |
cadf73c6 | 880 | for (i = 0; i < 4; i ++) |
881 | if ((netip6[i] & iface->mask.ipv6.sin6_addr.s6_addr32[i]) != | |
882 | (iface->address.ipv6.sin6_addr.s6_addr32[i] & | |
883 | iface->mask.ipv6.sin6_addr.s6_addr32[i])) | |
884 | break; | |
20264b99 | 885 | |
cadf73c6 | 886 | if (i == 4) |
887 | return (1); | |
888 | } | |
a09840c5 | 889 | #endif /* AF_INET6 */ |
cadf73c6 | 890 | } |
20264b99 | 891 | } |
892 | break; | |
893 | ||
e5ebb675 | 894 | case AUTH_NAME : |
895 | /* | |
896 | * Check for exact name match... | |
897 | */ | |
898 | ||
f3e786fc | 899 | if (!strcasecmp(name, masks->mask.name.name)) |
e5ebb675 | 900 | return (1); |
901 | ||
902 | /* | |
903 | * Check for domain match... | |
904 | */ | |
905 | ||
906 | if (name_len >= masks->mask.name.length && | |
907 | masks->mask.name.name[0] == '.' && | |
ad4b44a5 | 908 | !strcasecmp(name + name_len - masks->mask.name.length, |
909 | masks->mask.name.name)) | |
e5ebb675 | 910 | return (1); |
911 | break; | |
912 | ||
913 | case AUTH_IP : | |
914 | /* | |
915 | * Check for IP/network address match... | |
916 | */ | |
917 | ||
99de6da0 | 918 | for (i = 0; i < 4; i ++) |
f3e786fc | 919 | if ((ip[i] & masks->mask.ip.netmask[i]) != |
920 | masks->mask.ip.address[i]) | |
99de6da0 | 921 | break; |
922 | ||
923 | if (i == 4) | |
e5ebb675 | 924 | return (1); |
925 | break; | |
926 | } | |
927 | ||
928 | masks ++; | |
929 | num_masks --; | |
930 | } | |
931 | ||
932 | return (0); | |
933 | } | |
934 | ||
935 | ||
9d0c9f28 | 936 | /* |
937 | * 'cupsdCheckGroup()' - Check for a user's group membership. | |
938 | */ | |
939 | ||
940 | int /* O - 1 if user is a member, 0 otherwise */ | |
941 | cupsdCheckGroup( | |
942 | const char *username, /* I - User name */ | |
943 | struct passwd *user, /* I - System user info */ | |
944 | const char *groupname) /* I - Group name */ | |
945 | { | |
946 | int i; /* Looping var */ | |
947 | struct group *group; /* System group info */ | |
948 | char junk[33]; /* MD5 password (not used) */ | |
949 | ||
950 | ||
f3e786fc | 951 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
952 | "cupsdCheckGroup(username=\"%s\", user=%p, groupname=\"%s\")", | |
953 | username, user, groupname); | |
9d0c9f28 | 954 | |
955 | /* | |
956 | * Validate input... | |
957 | */ | |
958 | ||
959 | if (!username || !groupname) | |
960 | return (0); | |
961 | ||
962 | /* | |
963 | * Check to see if the user is a member of the named group... | |
964 | */ | |
965 | ||
966 | group = getgrnam(groupname); | |
967 | endgrent(); | |
968 | ||
969 | if (group != NULL) | |
970 | { | |
971 | /* | |
972 | * Group exists, check it... | |
973 | */ | |
974 | ||
975 | for (i = 0; group->gr_mem[i]; i ++) | |
976 | if (!strcasecmp(username, group->gr_mem[i])) | |
977 | return (1); | |
978 | } | |
979 | ||
980 | /* | |
981 | * Group doesn't exist or user not in group list, check the group ID | |
982 | * against the user's group ID... | |
983 | */ | |
984 | ||
985 | if (user && group && group->gr_gid == user->pw_gid) | |
986 | return (1); | |
987 | ||
988 | /* | |
989 | * Username not found, group not found, or user is not part of the | |
990 | * system group... Check for a user and group in the MD5 password | |
991 | * file... | |
992 | */ | |
993 | ||
589eb420 | 994 | if (cupsdGetMD5Passwd(username, groupname, junk) != NULL) |
9d0c9f28 | 995 | return (1); |
996 | ||
997 | /* | |
998 | * If we get this far, then the user isn't part of the named group... | |
999 | */ | |
1000 | ||
1001 | return (0); | |
1002 | } | |
1003 | ||
1004 | ||
89db771d | 1005 | /* |
589eb420 | 1006 | * 'cupsdCopyLocation()' - Make a copy of a location... |
89db771d | 1007 | */ |
1008 | ||
589eb420 | 1009 | cupsd_location_t * /* O - New location */ |
f3e786fc | 1010 | cupsdCopyLocation( |
1011 | cupsd_location_t **loc) /* IO - Original location */ | |
89db771d | 1012 | { |
f3e786fc | 1013 | int i; /* Looping var */ |
589eb420 | 1014 | cupsd_location_t *temp; /* New location */ |
f3e786fc | 1015 | char location[HTTP_MAX_URI]; |
1016 | /* Location of resource */ | |
89db771d | 1017 | |
1018 | ||
985a12c2 | 1019 | /* |
589eb420 | 1020 | * Use a local copy of location because cupsdAddLocation may cause |
985a12c2 | 1021 | * this memory to be moved... |
1022 | */ | |
1023 | ||
1024 | strlcpy(location, (*loc)->location, sizeof(location)); | |
1025 | ||
589eb420 | 1026 | if ((temp = cupsdAddLocation(location)) == NULL) |
89db771d | 1027 | return (NULL); |
1028 | ||
89db771d | 1029 | /* |
1030 | * Copy the information from the original location to the new one. | |
1031 | */ | |
1032 | ||
9d0c9f28 | 1033 | temp->limit = (*loc)->limit; |
89db771d | 1034 | temp->order_type = (*loc)->order_type; |
1035 | temp->type = (*loc)->type; | |
1036 | temp->level = (*loc)->level; | |
1037 | temp->satisfy = (*loc)->satisfy; | |
1038 | temp->encryption = (*loc)->encryption; | |
1039 | ||
1040 | if ((temp->num_names = (*loc)->num_names) > 0) | |
1041 | { | |
1042 | /* | |
1043 | * Copy the names array... | |
1044 | */ | |
1045 | ||
1046 | if ((temp->names = calloc(temp->num_names, sizeof(char *))) == NULL) | |
1047 | { | |
f3e786fc | 1048 | cupsdLogMessage(CUPSD_LOG_ERROR, |
1049 | "cupsdCopyLocation: Unable to allocate memory for %d names: %s", | |
1050 | temp->num_names, strerror(errno)); | |
ad4b44a5 | 1051 | |
1052 | cupsdDeleteLocation(temp); | |
89db771d | 1053 | return (NULL); |
1054 | } | |
1055 | ||
1056 | for (i = 0; i < temp->num_names; i ++) | |
1057 | if ((temp->names[i] = strdup((*loc)->names[i])) == NULL) | |
1058 | { | |
f3e786fc | 1059 | cupsdLogMessage(CUPSD_LOG_ERROR, |
1060 | "cupsdCopyLocation: Unable to copy name \"%s\": %s", | |
1061 | (*loc)->names[i], strerror(errno)); | |
89db771d | 1062 | |
ad4b44a5 | 1063 | cupsdDeleteLocation(temp); |
89db771d | 1064 | return (NULL); |
1065 | } | |
1066 | } | |
1067 | ||
1068 | if ((temp->num_allow = (*loc)->num_allow) > 0) | |
1069 | { | |
1070 | /* | |
1071 | * Copy allow rules... | |
1072 | */ | |
1073 | ||
589eb420 | 1074 | if ((temp->allow = calloc(temp->num_allow, sizeof(cupsd_authmask_t))) == NULL) |
89db771d | 1075 | { |
f3e786fc | 1076 | cupsdLogMessage(CUPSD_LOG_ERROR, |
1077 | "cupsdCopyLocation: Unable to allocate memory for %d allow rules: %s", | |
1078 | temp->num_allow, strerror(errno)); | |
ad4b44a5 | 1079 | cupsdDeleteLocation(temp); |
89db771d | 1080 | return (NULL); |
1081 | } | |
1082 | ||
1083 | for (i = 0; i < temp->num_allow; i ++) | |
1084 | switch (temp->allow[i].type = (*loc)->allow[i].type) | |
1085 | { | |
1086 | case AUTH_NAME : | |
1087 | temp->allow[i].mask.name.length = (*loc)->allow[i].mask.name.length; | |
1088 | temp->allow[i].mask.name.name = strdup((*loc)->allow[i].mask.name.name); | |
1089 | ||
1090 | if (temp->allow[i].mask.name.name == NULL) | |
1091 | { | |
f3e786fc | 1092 | cupsdLogMessage(CUPSD_LOG_ERROR, |
1093 | "cupsdCopyLocation: Unable to copy allow name \"%s\": %s", | |
1094 | (*loc)->allow[i].mask.name.name, strerror(errno)); | |
ad4b44a5 | 1095 | cupsdDeleteLocation(temp); |
89db771d | 1096 | return (NULL); |
1097 | } | |
1098 | break; | |
1099 | case AUTH_IP : | |
1100 | memcpy(&(temp->allow[i].mask.ip), &((*loc)->allow[i].mask.ip), | |
589eb420 | 1101 | sizeof(cupsd_ipmask_t)); |
89db771d | 1102 | break; |
1103 | } | |
1104 | } | |
1105 | ||
1106 | if ((temp->num_deny = (*loc)->num_deny) > 0) | |
1107 | { | |
1108 | /* | |
1109 | * Copy deny rules... | |
1110 | */ | |
1111 | ||
589eb420 | 1112 | if ((temp->deny = calloc(temp->num_deny, sizeof(cupsd_authmask_t))) == NULL) |
89db771d | 1113 | { |
f3e786fc | 1114 | cupsdLogMessage(CUPSD_LOG_ERROR, |
1115 | "cupsdCopyLocation: Unable to allocate memory for %d deny rules: %s", | |
1116 | temp->num_deny, strerror(errno)); | |
ad4b44a5 | 1117 | cupsdDeleteLocation(temp); |
89db771d | 1118 | return (NULL); |
1119 | } | |
1120 | ||
1121 | for (i = 0; i < temp->num_deny; i ++) | |
1122 | switch (temp->deny[i].type = (*loc)->deny[i].type) | |
1123 | { | |
1124 | case AUTH_NAME : | |
1125 | temp->deny[i].mask.name.length = (*loc)->deny[i].mask.name.length; | |
1126 | temp->deny[i].mask.name.name = strdup((*loc)->deny[i].mask.name.name); | |
1127 | ||
1128 | if (temp->deny[i].mask.name.name == NULL) | |
1129 | { | |
f3e786fc | 1130 | cupsdLogMessage(CUPSD_LOG_ERROR, |
1131 | "cupsdCopyLocation: Unable to copy deny name \"%s\": %s", | |
1132 | (*loc)->deny[i].mask.name.name, strerror(errno)); | |
ad4b44a5 | 1133 | cupsdDeleteLocation(temp); |
89db771d | 1134 | return (NULL); |
1135 | } | |
1136 | break; | |
1137 | case AUTH_IP : | |
1138 | memcpy(&(temp->deny[i].mask.ip), &((*loc)->deny[i].mask.ip), | |
589eb420 | 1139 | sizeof(cupsd_ipmask_t)); |
89db771d | 1140 | break; |
1141 | } | |
1142 | } | |
1143 | ||
1144 | return (temp); | |
1145 | } | |
1146 | ||
1147 | ||
fd8b1cf8 | 1148 | /* |
589eb420 | 1149 | * 'cupsdDeleteAllLocations()' - Free all memory used for location authorization. |
fd8b1cf8 | 1150 | */ |
1151 | ||
1152 | void | |
589eb420 | 1153 | cupsdDeleteAllLocations(void) |
fd8b1cf8 | 1154 | { |
f3e786fc | 1155 | cupsd_location_t *loc; /* Current location */ |
fd8b1cf8 | 1156 | |
1157 | ||
1158 | /* | |
1159 | * Free all of the allow/deny records first... | |
1160 | */ | |
1161 | ||
ad4b44a5 | 1162 | for (loc = (cupsd_location_t *)cupsArrayFirst(Locations); |
1163 | loc; | |
1164 | loc = (cupsd_location_t *)cupsArrayNext(Locations)) | |
99baf768 | 1165 | cupsdDeleteLocation(loc); |
fd8b1cf8 | 1166 | |
1167 | /* | |
1168 | * Then free the location array... | |
1169 | */ | |
1170 | ||
ad4b44a5 | 1171 | cupsArrayDelete(Locations); |
1172 | Locations = NULL; | |
fd8b1cf8 | 1173 | } |
1174 | ||
1175 | ||
99baf768 | 1176 | /* |
1177 | * 'cupsdDeleteLocation()' - Free all memory used by a location. | |
1178 | */ | |
1179 | ||
1180 | void | |
f3e786fc | 1181 | cupsdDeleteLocation( |
1182 | cupsd_location_t *loc) /* I - Location to delete */ | |
99baf768 | 1183 | { |
f3e786fc | 1184 | int i; /* Looping var */ |
1185 | cupsd_authmask_t *mask; /* Current mask */ | |
99baf768 | 1186 | |
1187 | ||
ad4b44a5 | 1188 | cupsArrayRemove(Locations, loc); |
1189 | ||
99baf768 | 1190 | for (i = loc->num_names - 1; i >= 0; i --) |
1191 | free(loc->names[i]); | |
1192 | ||
1193 | if (loc->num_names > 0) | |
1194 | free(loc->names); | |
1195 | ||
1196 | for (i = loc->num_allow, mask = loc->allow; i > 0; i --, mask ++) | |
1197 | if (mask->type == AUTH_NAME || mask->type == AUTH_INTERFACE) | |
1198 | free(mask->mask.name.name); | |
1199 | ||
1200 | if (loc->num_allow > 0) | |
1201 | free(loc->allow); | |
1202 | ||
1203 | for (i = loc->num_deny, mask = loc->deny; i > 0; i --, mask ++) | |
1204 | if (mask->type == AUTH_NAME || mask->type == AUTH_INTERFACE) | |
1205 | free(mask->mask.name.name); | |
1206 | ||
1207 | if (loc->num_deny > 0) | |
1208 | free(loc->deny); | |
ad4b44a5 | 1209 | |
1210 | free(loc->location); | |
1211 | free(loc); | |
99baf768 | 1212 | } |
1213 | ||
1214 | ||
fd8b1cf8 | 1215 | /* |
f3e786fc | 1216 | * 'cupsdDenyHost()' - Add a host name that is not allowed to access the |
1217 | * location. | |
fd8b1cf8 | 1218 | */ |
1219 | ||
1220 | void | |
589eb420 | 1221 | cupsdDenyHost(cupsd_location_t *loc, /* I - Location to add to */ |
f3e786fc | 1222 | char *name) /* I - Name of host or domain to add */ |
fd8b1cf8 | 1223 | { |
589eb420 | 1224 | cupsd_authmask_t *temp; /* New host/domain mask */ |
f3e786fc | 1225 | char ifname[32], /* Interface name */ |
1226 | *ifptr; /* Pointer to end of name */ | |
fd8b1cf8 | 1227 | |
1228 | ||
f3e786fc | 1229 | cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDenyHost(loc=%p(%s), name=\"%s\")", |
1230 | loc, loc->location, name); | |
a8c7842b | 1231 | |
fd8b1cf8 | 1232 | if ((temp = add_deny(loc)) == NULL) |
1233 | return; | |
1234 | ||
ad4b44a5 | 1235 | if (!strcasecmp(name, "@LOCAL")) |
20264b99 | 1236 | { |
1237 | /* | |
1238 | * Deny *interface*... | |
1239 | */ | |
1240 | ||
1241 | temp->type = AUTH_INTERFACE; | |
cadf73c6 | 1242 | temp->mask.name.name = strdup("*"); |
20264b99 | 1243 | temp->mask.name.length = 1; |
1244 | } | |
ad4b44a5 | 1245 | else if (!strncasecmp(name, "@IF(", 4)) |
20264b99 | 1246 | { |
1247 | /* | |
1248 | * Deny *interface*... | |
1249 | */ | |
1250 | ||
def978d5 | 1251 | strlcpy(ifname, name + 4, sizeof(ifname)); |
20264b99 | 1252 | |
1253 | ifptr = ifname + strlen(ifname); | |
1254 | ||
1255 | if (ifptr[-1] == ')') | |
1256 | { | |
1257 | ifptr --; | |
1258 | *ifptr = '\0'; | |
1259 | } | |
1260 | ||
1261 | temp->type = AUTH_INTERFACE; | |
1262 | temp->mask.name.name = strdup(ifname); | |
1263 | temp->mask.name.length = ifptr - ifname; | |
1264 | } | |
1265 | else | |
1266 | { | |
1267 | /* | |
1268 | * Deny name... | |
1269 | */ | |
1270 | ||
1271 | temp->type = AUTH_NAME; | |
1272 | temp->mask.name.name = strdup(name); | |
1273 | temp->mask.name.length = strlen(name); | |
1274 | } | |
fd8b1cf8 | 1275 | } |
1276 | ||
1277 | ||
1278 | /* | |
f3e786fc | 1279 | * 'cupsdDenyIP()' - Add an IP address or network that is not allowed to |
1280 | * access the location. | |
fd8b1cf8 | 1281 | */ |
1282 | ||
1283 | void | |
f3e786fc | 1284 | cupsdDenyIP(cupsd_location_t *loc, /* I - Location to add to */ |
1285 | unsigned address[4],/* I - IP address to add */ | |
1286 | unsigned netmask[4])/* I - Netmask of address */ | |
fd8b1cf8 | 1287 | { |
589eb420 | 1288 | cupsd_authmask_t *temp; /* New host/domain mask */ |
fd8b1cf8 | 1289 | |
1290 | ||
f3e786fc | 1291 | cupsdLogMessage(CUPSD_LOG_DEBUG, |
1292 | "cupsdDenyIP(loc=%p(%s), address=%x:%x:%x:%x, netmask=%x:%x:%x:%x)", | |
1293 | loc, loc->location, address[0], address[1], address[2], | |
1294 | address[3], netmask[0], netmask[1], netmask[2], | |
1295 | netmask[3]); | |
a8c7842b | 1296 | |
fd8b1cf8 | 1297 | if ((temp = add_deny(loc)) == NULL) |
1298 | return; | |
1299 | ||
99de6da0 | 1300 | temp->type = AUTH_IP; |
a8c7842b | 1301 | memcpy(temp->mask.ip.address, address, sizeof(temp->mask.ip.address)); |
1302 | memcpy(temp->mask.ip.netmask, netmask, sizeof(temp->mask.ip.netmask)); | |
fd8b1cf8 | 1303 | } |
1304 | ||
1305 | ||
83e740a5 | 1306 | /* |
589eb420 | 1307 | * 'cupsdFindBest()' - Find the location entry that best matches the resource. |
83e740a5 | 1308 | */ |
1309 | ||
f3e786fc | 1310 | cupsd_location_t * /* O - Location that matches */ |
1311 | cupsdFindBest(const char *path, /* I - Resource path */ | |
1312 | http_state_t state) /* I - HTTP state/request */ | |
83e740a5 | 1313 | { |
f3e786fc | 1314 | char uri[HTTP_MAX_URI], |
1315 | /* URI in request... */ | |
1316 | *uriptr; /* Pointer into URI */ | |
1317 | cupsd_location_t *loc, /* Current location */ | |
1318 | *best; /* Best match for location so far */ | |
1319 | int bestlen; /* Length of best match */ | |
1320 | int limit; /* Limit field */ | |
1321 | static const int limits[] = /* Map http_status_t to AUTH_LIMIT_xyz */ | |
46490d9d | 1322 | { |
1323 | AUTH_LIMIT_ALL, | |
1324 | AUTH_LIMIT_OPTIONS, | |
1325 | AUTH_LIMIT_GET, | |
1326 | AUTH_LIMIT_GET, | |
1327 | AUTH_LIMIT_HEAD, | |
1328 | AUTH_LIMIT_POST, | |
1329 | AUTH_LIMIT_POST, | |
1330 | AUTH_LIMIT_POST, | |
1331 | AUTH_LIMIT_PUT, | |
1332 | AUTH_LIMIT_PUT, | |
1333 | AUTH_LIMIT_DELETE, | |
1334 | AUTH_LIMIT_TRACE, | |
1335 | AUTH_LIMIT_ALL, | |
1336 | AUTH_LIMIT_ALL | |
1337 | }; | |
83e740a5 | 1338 | |
1339 | ||
753453e4 | 1340 | /* |
1341 | * First copy the connection URI to a local string so we have drop | |
1342 | * any .ppd extension from the pathname in /printers or /classes | |
1343 | * URIs... | |
1344 | */ | |
1345 | ||
def978d5 | 1346 | strlcpy(uri, path, sizeof(uri)); |
753453e4 | 1347 | |
c10bce69 | 1348 | if (!strncmp(uri, "/printers/", 10) || |
1349 | !strncmp(uri, "/classes/", 9)) | |
753453e4 | 1350 | { |
1351 | /* | |
1352 | * Check if the URI has .ppd on the end... | |
1353 | */ | |
1354 | ||
1355 | uriptr = uri + strlen(uri) - 4; /* len > 4 if we get here... */ | |
1356 | ||
c10bce69 | 1357 | if (!strcmp(uriptr, ".ppd")) |
753453e4 | 1358 | *uriptr = '\0'; |
1359 | } | |
1360 | ||
f3e786fc | 1361 | cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdFindBest: uri = \"%s\"...", uri); |
753453e4 | 1362 | |
83e740a5 | 1363 | /* |
1364 | * Loop through the list of locations to find a match... | |
1365 | */ | |
1366 | ||
753453e4 | 1367 | limit = limits[state]; |
83e740a5 | 1368 | best = NULL; |
1369 | bestlen = 0; | |
1370 | ||
ad4b44a5 | 1371 | for (loc = (cupsd_location_t *)cupsArrayFirst(Locations); |
1372 | loc; | |
1373 | loc = (cupsd_location_t *)cupsArrayNext(Locations)) | |
89db771d | 1374 | { |
f3e786fc | 1375 | cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdFindBest: Location %s Limit %x", |
1376 | loc->location, loc->limit); | |
89db771d | 1377 | |
c10bce69 | 1378 | if (!strncmp(uri, "/printers/", 10) || !strncmp(uri, "/classes/", 9)) |
83e740a5 | 1379 | { |
897922a9 | 1380 | /* |
1381 | * Use case-insensitive comparison for queue names... | |
1382 | */ | |
1383 | ||
1384 | if (loc->length > bestlen && | |
ad4b44a5 | 1385 | !strncasecmp(uri, loc->location, loc->length) && |
897922a9 | 1386 | loc->location[0] == '/' && |
1387 | (limit & loc->limit) != 0) | |
1388 | { | |
1389 | best = loc; | |
1390 | bestlen = loc->length; | |
1391 | } | |
1392 | } | |
1393 | else | |
1394 | { | |
1395 | /* | |
1396 | * Use case-sensitive comparison for other URIs... | |
1397 | */ | |
1398 | ||
1399 | if (loc->length > bestlen && | |
c10bce69 | 1400 | !strncmp(uri, loc->location, loc->length) && |
897922a9 | 1401 | loc->location[0] == '/' && |
1402 | (limit & loc->limit) != 0) | |
1403 | { | |
1404 | best = loc; | |
1405 | bestlen = loc->length; | |
1406 | } | |
83e740a5 | 1407 | } |
89db771d | 1408 | } |
83e740a5 | 1409 | |
1410 | /* | |
1411 | * Return the match, if any... | |
1412 | */ | |
1413 | ||
f3e786fc | 1414 | cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdFindBest: best = %s", |
1415 | best ? best->location : "NONE"); | |
b5cb0608 | 1416 | |
83e740a5 | 1417 | return (best); |
1418 | } | |
1419 | ||
1420 | ||
e4f4eb8e | 1421 | /* |
589eb420 | 1422 | * 'cupsdFindLocation()' - Find the named location. |
e4f4eb8e | 1423 | */ |
1424 | ||
f3e786fc | 1425 | cupsd_location_t * /* O - Location that matches */ |
589eb420 | 1426 | cupsdFindLocation(const char *location) /* I - Connection */ |
e4f4eb8e | 1427 | { |
ad4b44a5 | 1428 | cupsd_location_t key; /* Search key */ |
e4f4eb8e | 1429 | |
1430 | ||
ad4b44a5 | 1431 | key.location = (char *)location; |
e4f4eb8e | 1432 | |
ad4b44a5 | 1433 | return ((cupsd_location_t *)cupsArrayFind(Locations, &key)); |
e4f4eb8e | 1434 | } |
1435 | ||
1436 | ||
19e7b382 | 1437 | /* |
589eb420 | 1438 | * 'cupsdGetMD5Passwd()' - Get an MD5 password. |
19e7b382 | 1439 | */ |
1440 | ||
1441 | char * /* O - MD5 password string */ | |
589eb420 | 1442 | cupsdGetMD5Passwd(const char *username, /* I - Username */ |
f3e786fc | 1443 | const char *group, /* I - Group */ |
1444 | char passwd[33])/* O - MD5 password string */ | |
19e7b382 | 1445 | { |
7b0fde61 | 1446 | cups_file_t *fp; /* passwd.md5 file */ |
1447 | char filename[1024], /* passwd.md5 filename */ | |
1448 | line[256], /* Line from file */ | |
1449 | tempuser[33], /* User from file */ | |
1450 | tempgroup[33]; /* Group from file */ | |
19e7b382 | 1451 | |
1452 | ||
f3e786fc | 1453 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
1454 | "cupsdGetMD5Passwd(username=\"%s\", group=\"%s\", passwd=%p)", | |
1455 | username, group ? group : "(null)", passwd); | |
9c4b5e2e | 1456 | |
19e7b382 | 1457 | snprintf(filename, sizeof(filename), "%s/passwd.md5", ServerRoot); |
7b0fde61 | 1458 | if ((fp = cupsFileOpen(filename, "r")) == NULL) |
9c4b5e2e | 1459 | { |
3fbe1135 | 1460 | if (errno != ENOENT) |
1461 | cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to open %s - %s", filename, | |
1462 | strerror(errno)); | |
1463 | ||
19e7b382 | 1464 | return (NULL); |
9c4b5e2e | 1465 | } |
19e7b382 | 1466 | |
7b0fde61 | 1467 | while (cupsFileGets(fp, line, sizeof(line)) != NULL) |
19e7b382 | 1468 | { |
1469 | if (sscanf(line, "%32[^:]:%32[^:]:%32s", tempuser, tempgroup, passwd) != 3) | |
9c4b5e2e | 1470 | { |
f3e786fc | 1471 | cupsdLogMessage(CUPSD_LOG_ERROR, "Bad MD5 password line: %s", line); |
19e7b382 | 1472 | continue; |
9c4b5e2e | 1473 | } |
19e7b382 | 1474 | |
ad4b44a5 | 1475 | if (!strcmp(username, tempuser) && |
1476 | (group == NULL || !strcmp(group, tempgroup))) | |
19e7b382 | 1477 | { |
1478 | /* | |
1479 | * Found the password entry! | |
1480 | */ | |
1481 | ||
f3e786fc | 1482 | cupsdLogMessage(CUPSD_LOG_DEBUG2, "Found MD5 user %s, group %s...", |
1483 | username, tempgroup); | |
9c4b5e2e | 1484 | |
7b0fde61 | 1485 | cupsFileClose(fp); |
19e7b382 | 1486 | return (passwd); |
1487 | } | |
1488 | } | |
1489 | ||
1490 | /* | |
1491 | * Didn't find a password entry - return NULL! | |
1492 | */ | |
1493 | ||
7b0fde61 | 1494 | cupsFileClose(fp); |
19e7b382 | 1495 | return (NULL); |
1496 | } | |
1497 | ||
1498 | ||
fd8b1cf8 | 1499 | /* |
99baf768 | 1500 | * 'cupsdIsAuthorized()' - Check to see if the user is authorized... |
fd8b1cf8 | 1501 | */ |
1502 | ||
99baf768 | 1503 | http_status_t /* O - HTTP_OK if authorized or error code */ |
f3e786fc | 1504 | cupsdIsAuthorized(cupsd_client_t *con, /* I - Connection */ |
1505 | const char *owner)/* I - Owner of object */ | |
fd8b1cf8 | 1506 | { |
f3e786fc | 1507 | int i, j, /* Looping vars */ |
1508 | auth; /* Authorization status */ | |
1509 | unsigned address[4]; /* Authorization address */ | |
1510 | cupsd_location_t *best; /* Best match for location so far */ | |
1511 | int hostlen; /* Length of hostname */ | |
b7fc00ef | 1512 | const char *username; /* Username to authorize */ |
f3e786fc | 1513 | struct passwd *pw; /* User password data */ |
a8c7842b | 1514 | static const char * const levels[] = /* Auth levels */ |
1515 | { | |
1516 | "ANON", | |
1517 | "USER", | |
1518 | "GROUP" | |
1519 | }; | |
1520 | static const char * const types[] = /* Auth types */ | |
1521 | { | |
1522 | "NONE", | |
1523 | "BASIC", | |
1524 | "DIGEST", | |
1525 | "BASICDIGEST" | |
1526 | }; | |
fd8b1cf8 | 1527 | |
1528 | ||
f3e786fc | 1529 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
1530 | "cupsdIsAuthorized: con->uri=\"%s\", con->best=%p(%s)", | |
1531 | con->uri, con->best, con->best ? con->best->location : ""); | |
b7fc00ef | 1532 | if (owner) |
1533 | cupsdLogMessage(CUPSD_LOG_DEBUG2, | |
1534 | "cupsdIsAuthorized: owner=\"%s\"", owner); | |
b5cb0608 | 1535 | |
fd8b1cf8 | 1536 | /* |
99baf768 | 1537 | * If there is no "best" authentication rule for this request, then |
a8c7842b | 1538 | * access is allowed from the local system and denied from other |
1539 | * addresses... | |
fd8b1cf8 | 1540 | */ |
1541 | ||
a8c7842b | 1542 | if (!con->best) |
8c367b6f | 1543 | { |
1544 | if (!strcmp(con->http.hostname, "localhost") || | |
1545 | !strcmp(con->http.hostname, ServerName)) | |
1546 | return (HTTP_OK); | |
1547 | else | |
1548 | return (HTTP_FORBIDDEN); | |
1549 | } | |
fd8b1cf8 | 1550 | |
99baf768 | 1551 | best = con->best; |
1552 | ||
f3e786fc | 1553 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
1554 | "cupsdIsAuthorized: level=AUTH_%s, type=AUTH_%s, satisfy=AUTH_SATISFY_%s, num_names=%d", | |
1555 | levels[best->level], types[best->type], | |
1556 | best->satisfy ? "ANY" : "ALL", best->num_names); | |
0051f336 | 1557 | |
1558 | if (best->limit == AUTH_LIMIT_IPP) | |
f3e786fc | 1559 | cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: op=%x(%s)", |
1560 | best->op, ippOpString(best->op)); | |
0051f336 | 1561 | |
fd8b1cf8 | 1562 | /* |
1563 | * Check host/ip-based accesses... | |
1564 | */ | |
1565 | ||
99de6da0 | 1566 | #ifdef AF_INET6 |
086c584d | 1567 | if (con->http.hostaddr->addr.sa_family == AF_INET6) |
99de6da0 | 1568 | { |
7c298ddc | 1569 | /* |
1570 | * Copy IPv6 address... | |
1571 | */ | |
1572 | ||
086c584d | 1573 | address[0] = ntohl(con->http.hostaddr->ipv6.sin6_addr.s6_addr32[0]); |
1574 | address[1] = ntohl(con->http.hostaddr->ipv6.sin6_addr.s6_addr32[1]); | |
1575 | address[2] = ntohl(con->http.hostaddr->ipv6.sin6_addr.s6_addr32[2]); | |
1576 | address[3] = ntohl(con->http.hostaddr->ipv6.sin6_addr.s6_addr32[3]); | |
99de6da0 | 1577 | } |
1578 | else | |
1579 | #endif /* AF_INET6 */ | |
086c584d | 1580 | if (con->http.hostaddr->addr.sa_family == AF_INET) |
99de6da0 | 1581 | { |
99de6da0 | 1582 | /* |
7c298ddc | 1583 | * Copy IPv4 address... |
99de6da0 | 1584 | */ |
1585 | ||
7c298ddc | 1586 | address[0] = 0; |
1587 | address[1] = 0; | |
1588 | address[2] = 0; | |
086c584d | 1589 | address[3] = ntohl(con->http.hostaddr->ipv4.sin_addr.s_addr); |
99de6da0 | 1590 | } |
1591 | else | |
1592 | memset(address, 0, sizeof(address)); | |
1593 | ||
a74b005d | 1594 | hostlen = strlen(con->http.hostname); |
fd8b1cf8 | 1595 | |
99baf768 | 1596 | if (!strcasecmp(con->http.hostname, "localhost")) |
fd8b1cf8 | 1597 | { |
472ce1dc | 1598 | /* |
99baf768 | 1599 | * Access from localhost (127.0.0.1 or :::1) is always allowed... |
472ce1dc | 1600 | */ |
1601 | ||
1602 | auth = AUTH_ALLOW; | |
1603 | } | |
1604 | else | |
1605 | { | |
1606 | /* | |
1607 | * Do authorization checks on the domain/address... | |
1608 | */ | |
1609 | ||
d21a7597 | 1610 | switch (best->order_type) |
472ce1dc | 1611 | { |
d21a7597 | 1612 | default : |
1613 | auth = AUTH_DENY; /* anti-compiler-warning-code */ | |
1614 | break; | |
1615 | ||
472ce1dc | 1616 | case AUTH_ALLOW : /* Order Deny,Allow */ |
d21a7597 | 1617 | auth = AUTH_ALLOW; |
1618 | ||
589eb420 | 1619 | if (cupsdCheckAuth(address, con->http.hostname, hostlen, |
e5ebb675 | 1620 | best->num_deny, best->deny)) |
472ce1dc | 1621 | auth = AUTH_DENY; |
1622 | ||
589eb420 | 1623 | if (cupsdCheckAuth(address, con->http.hostname, hostlen, |
e5ebb675 | 1624 | best->num_allow, best->allow)) |
472ce1dc | 1625 | auth = AUTH_ALLOW; |
1626 | break; | |
1627 | ||
1628 | case AUTH_DENY : /* Order Allow,Deny */ | |
d21a7597 | 1629 | auth = AUTH_DENY; |
1630 | ||
589eb420 | 1631 | if (cupsdCheckAuth(address, con->http.hostname, hostlen, |
e5ebb675 | 1632 | best->num_allow, best->allow)) |
472ce1dc | 1633 | auth = AUTH_ALLOW; |
1634 | ||
589eb420 | 1635 | if (cupsdCheckAuth(address, con->http.hostname, hostlen, |
e5ebb675 | 1636 | best->num_deny, best->deny)) |
472ce1dc | 1637 | auth = AUTH_DENY; |
1638 | break; | |
1639 | } | |
fd8b1cf8 | 1640 | } |
1641 | ||
f3e786fc | 1642 | cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: auth=AUTH_%s...", |
1643 | auth ? "DENY" : "ALLOW"); | |
b5cb0608 | 1644 | |
46490d9d | 1645 | if (auth == AUTH_DENY && best->satisfy == AUTH_SATISFY_ALL) |
fd8b1cf8 | 1646 | return (HTTP_FORBIDDEN); |
1647 | ||
bcf61448 | 1648 | #ifdef HAVE_SSL |
a75c006a | 1649 | /* |
1650 | * See if encryption is required... | |
1651 | */ | |
1652 | ||
2a0ef17a | 1653 | if (best->encryption >= HTTP_ENCRYPT_REQUIRED && !con->http.tls) |
b5cb0608 | 1654 | { |
f3e786fc | 1655 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
1656 | "cupsdIsAuthorized: Need upgrade to TLS..."); | |
a75c006a | 1657 | return (HTTP_UPGRADE_REQUIRED); |
b5cb0608 | 1658 | } |
bcf61448 | 1659 | #endif /* HAVE_SSL */ |
a75c006a | 1660 | |
fd8b1cf8 | 1661 | /* |
1662 | * Now see what access level is required... | |
1663 | */ | |
1664 | ||
0051f336 | 1665 | if (best->level == AUTH_ANON || /* Anonymous access - allow it */ |
1666 | (best->type == AUTH_NONE && best->num_names == 0)) | |
1667 | return (HTTP_OK); | |
1668 | ||
b7fc00ef | 1669 | if (!con->username[0] && best->type == AUTH_NONE && |
1670 | best->limit == AUTH_LIMIT_IPP) | |
a8c7842b | 1671 | { |
1672 | /* | |
1673 | * Check for unauthenticated username... | |
1674 | */ | |
1675 | ||
1676 | ipp_attribute_t *attr; /* requesting-user-name attribute */ | |
1677 | ||
1678 | ||
1679 | attr = ippFindAttribute(con->request, "requesting-user-name", IPP_TAG_NAME); | |
1680 | if (attr) | |
1681 | { | |
f3e786fc | 1682 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
1683 | "cupsdIsAuthorized: requesting-user-name=\"%s\"", | |
1684 | attr->values[0].string.text); | |
b7fc00ef | 1685 | username = attr->values[0].string.text; |
a8c7842b | 1686 | } |
b7fc00ef | 1687 | else if (best->satisfy == AUTH_SATISFY_ALL || auth == AUTH_DENY) |
46490d9d | 1688 | return (HTTP_UNAUTHORIZED); /* Non-anonymous needs user/pass */ |
1689 | else | |
1690 | return (HTTP_OK); /* unless overridden with Satisfy */ | |
1691 | } | |
b7fc00ef | 1692 | else |
1693 | { | |
1694 | cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: username=\"%s\"", | |
1695 | con->username); | |
1696 | ||
1697 | if (!con->username[0]) | |
1698 | { | |
1699 | if (best->satisfy == AUTH_SATISFY_ALL || auth == AUTH_DENY) | |
1700 | return (HTTP_UNAUTHORIZED); /* Non-anonymous needs user/pass */ | |
1701 | else | |
1702 | return (HTTP_OK); /* unless overridden with Satisfy */ | |
1703 | } | |
1704 | ||
1705 | username = con->username; | |
1706 | } | |
fd8b1cf8 | 1707 | |
fd8b1cf8 | 1708 | /* |
b7fc00ef | 1709 | * OK, got a username. See if we need normal user access, or group |
d62e9bdd | 1710 | * access... (root always matches) |
fd8b1cf8 | 1711 | */ |
1712 | ||
b7fc00ef | 1713 | if (!strcmp(username, "root")) |
fd8b1cf8 | 1714 | return (HTTP_OK); |
1715 | ||
ff84728d | 1716 | /* |
1717 | * Get the user info... | |
1718 | */ | |
1719 | ||
b7fc00ef | 1720 | pw = getpwnam(username); |
ff84728d | 1721 | endpwent(); |
1722 | ||
46490d9d | 1723 | if (best->level == AUTH_USER) |
f3d580b9 | 1724 | { |
46490d9d | 1725 | /* |
1726 | * If there are no names associated with this location, then | |
1727 | * any valid user is OK... | |
1728 | */ | |
fd8b1cf8 | 1729 | |
46490d9d | 1730 | if (best->num_names == 0) |
fd8b1cf8 | 1731 | return (HTTP_OK); |
1732 | ||
46490d9d | 1733 | /* |
1734 | * Otherwise check the user list and return OK if this user is | |
1735 | * allowed... | |
1736 | */ | |
1737 | ||
f3e786fc | 1738 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
1739 | "cupsdIsAuthorized: Checking user membership..."); | |
1740 | ||
46490d9d | 1741 | for (i = 0; i < best->num_names; i ++) |
f32b1ead | 1742 | { |
99baf768 | 1743 | if (!strcasecmp(best->names[i], "@OWNER") && owner && |
b7fc00ef | 1744 | !strcasecmp(username, owner)) |
f32b1ead | 1745 | return (HTTP_OK); |
99baf768 | 1746 | else if (!strcasecmp(best->names[i], "@SYSTEM")) |
1747 | { | |
f32b1ead | 1748 | for (j = 0; j < NumSystemGroups; j ++) |
b7fc00ef | 1749 | if (cupsdCheckGroup(username, pw, SystemGroups[j])) |
f32b1ead | 1750 | return (HTTP_OK); |
99baf768 | 1751 | } |
1752 | else if (best->names[i][0] == '@') | |
1753 | { | |
b7fc00ef | 1754 | if (cupsdCheckGroup(username, pw, best->names[i] + 1)) |
f32b1ead | 1755 | return (HTTP_OK); |
99baf768 | 1756 | } |
b7fc00ef | 1757 | else if (!strcasecmp(username, best->names[i])) |
46490d9d | 1758 | return (HTTP_OK); |
f32b1ead | 1759 | } |
46490d9d | 1760 | |
1761 | return (HTTP_UNAUTHORIZED); | |
1762 | } | |
1763 | ||
472ce1dc | 1764 | /* |
46490d9d | 1765 | * Check to see if this user is in any of the named groups... |
472ce1dc | 1766 | */ |
1767 | ||
f3e786fc | 1768 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
1769 | "cupsdIsAuthorized: Checking group membership..."); | |
b5cb0608 | 1770 | |
ff84728d | 1771 | /* |
1772 | * Check to see if this user is in any of the named groups... | |
1773 | */ | |
1774 | ||
1775 | for (i = 0; i < best->num_names; i ++) | |
46490d9d | 1776 | { |
ff84728d | 1777 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
1778 | "cupsdIsAuthorized: Checking group \"%s\" membership...", | |
1779 | best->names[i]); | |
753453e4 | 1780 | |
ff84728d | 1781 | if (!strcasecmp(best->names[i], "@SYSTEM")) |
46490d9d | 1782 | { |
ff84728d | 1783 | for (j = 0; j < NumSystemGroups; j ++) |
b7fc00ef | 1784 | if (cupsdCheckGroup(username, pw, SystemGroups[j])) |
ff84728d | 1785 | return (HTTP_OK); |
753453e4 | 1786 | } |
b7fc00ef | 1787 | else if (cupsdCheckGroup(username, pw, best->names[i])) |
ff84728d | 1788 | return (HTTP_OK); |
46490d9d | 1789 | } |
472ce1dc | 1790 | |
fd8b1cf8 | 1791 | /* |
ff84728d | 1792 | * The user isn't part of the specified group, so deny access... |
fd8b1cf8 | 1793 | */ |
1794 | ||
ff84728d | 1795 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
1796 | "cupsdIsAuthorized: User not in group(s)!"); | |
1797 | ||
1798 | return (HTTP_UNAUTHORIZED); | |
fd8b1cf8 | 1799 | } |
1800 | ||
1801 | ||
1802 | /* | |
1803 | * 'add_allow()' - Add an allow mask to the location. | |
1804 | */ | |
1805 | ||
589eb420 | 1806 | static cupsd_authmask_t * /* O - New mask record */ |
1807 | add_allow(cupsd_location_t *loc) /* I - Location to add to */ | |
fd8b1cf8 | 1808 | { |
589eb420 | 1809 | cupsd_authmask_t *temp; /* New mask record */ |
fd8b1cf8 | 1810 | |
1811 | ||
1812 | /* | |
1813 | * Range-check... | |
1814 | */ | |
1815 | ||
1816 | if (loc == NULL) | |
1817 | return (NULL); | |
1818 | ||
1819 | /* | |
1820 | * Try to allocate memory for the record... | |
1821 | */ | |
1822 | ||
1823 | if (loc->num_allow == 0) | |
589eb420 | 1824 | temp = malloc(sizeof(cupsd_authmask_t)); |
fd8b1cf8 | 1825 | else |
589eb420 | 1826 | temp = realloc(loc->allow, sizeof(cupsd_authmask_t) * (loc->num_allow + 1)); |
fd8b1cf8 | 1827 | |
1828 | if (temp == NULL) | |
1829 | return (NULL); | |
1830 | ||
1831 | loc->allow = temp; | |
1832 | temp += loc->num_allow; | |
1833 | loc->num_allow ++; | |
1834 | ||
1835 | /* | |
1836 | * Clear the mask record and return... | |
1837 | */ | |
1838 | ||
589eb420 | 1839 | memset(temp, 0, sizeof(cupsd_authmask_t)); |
fd8b1cf8 | 1840 | return (temp); |
1841 | } | |
1842 | ||
1843 | ||
1844 | /* | |
1845 | * 'add_deny()' - Add a deny mask to the location. | |
1846 | */ | |
1847 | ||
589eb420 | 1848 | static cupsd_authmask_t * /* O - New mask record */ |
f3e786fc | 1849 | add_deny(cupsd_location_t *loc) /* I - Location to add to */ |
fd8b1cf8 | 1850 | { |
589eb420 | 1851 | cupsd_authmask_t *temp; /* New mask record */ |
fd8b1cf8 | 1852 | |
1853 | ||
1854 | /* | |
1855 | * Range-check... | |
1856 | */ | |
1857 | ||
1858 | if (loc == NULL) | |
1859 | return (NULL); | |
1860 | ||
1861 | /* | |
1862 | * Try to allocate memory for the record... | |
1863 | */ | |
1864 | ||
1865 | if (loc->num_deny == 0) | |
589eb420 | 1866 | temp = malloc(sizeof(cupsd_authmask_t)); |
fd8b1cf8 | 1867 | else |
589eb420 | 1868 | temp = realloc(loc->deny, sizeof(cupsd_authmask_t) * (loc->num_deny + 1)); |
fd8b1cf8 | 1869 | |
1870 | if (temp == NULL) | |
1871 | return (NULL); | |
1872 | ||
1873 | loc->deny = temp; | |
1874 | temp += loc->num_deny; | |
1875 | loc->num_deny ++; | |
1876 | ||
1877 | /* | |
1878 | * Clear the mask record and return... | |
1879 | */ | |
1880 | ||
589eb420 | 1881 | memset(temp, 0, sizeof(cupsd_authmask_t)); |
fd8b1cf8 | 1882 | return (temp); |
1883 | } | |
1884 | ||
1885 | ||
ad4b44a5 | 1886 | /* |
1887 | * 'compare_locations()' - Compare two locations. | |
1888 | */ | |
1889 | ||
1890 | static int /* O - Result of comparison */ | |
1891 | compare_locations(cupsd_location_t *a, /* I - First location */ | |
1892 | cupsd_location_t *b) /* I - Second location */ | |
1893 | { | |
1894 | return (strcmp(b->location, a->location)); | |
1895 | } | |
1896 | ||
1897 | ||
753453e4 | 1898 | #if !HAVE_LIBPAM |
1899 | /* | |
1900 | * 'cups_crypt()' - Encrypt the password using the DES or MD5 algorithms, | |
1901 | * as needed. | |
1902 | */ | |
1903 | ||
69cf287c | 1904 | static char * /* O - Encrypted password */ |
1905 | cups_crypt(const char *pw, /* I - Password string */ | |
1906 | const char *salt) /* I - Salt (key) string */ | |
753453e4 | 1907 | { |
ad4b44a5 | 1908 | if (!strncmp(salt, "$1$", 3)) |
753453e4 | 1909 | { |
1910 | /* | |
1911 | * Use MD5 passwords without the benefit of PAM; this is for | |
1912 | * Slackware Linux, and the algorithm was taken from the | |
1913 | * old shadow-19990827/lib/md5crypt.c source code... :( | |
1914 | */ | |
1915 | ||
69cf287c | 1916 | int i; /* Looping var */ |
1917 | unsigned long n; /* Output number */ | |
1918 | int pwlen; /* Length of password string */ | |
1919 | const char *salt_end; /* End of "salt" data for MD5 */ | |
1920 | char *ptr; /* Pointer into result string */ | |
1921 | _cups_md5_state_t state; /* Primary MD5 state info */ | |
1922 | _cups_md5_state_t state2; /* Secondary MD5 state info */ | |
1923 | unsigned char digest[16]; /* MD5 digest result */ | |
1924 | static char result[120]; /* Final password string */ | |
753453e4 | 1925 | |
1926 | ||
1927 | /* | |
1928 | * Get the salt data between dollar signs, e.g. $1$saltdata$md5. | |
1929 | * Get a maximum of 8 characters of salt data after $1$... | |
1930 | */ | |
1931 | ||
1932 | for (salt_end = salt + 3; *salt_end && (salt_end - salt) < 11; salt_end ++) | |
1933 | if (*salt_end == '$') | |
1934 | break; | |
1935 | ||
1936 | /* | |
1937 | * Compute the MD5 sum we need... | |
1938 | */ | |
1939 | ||
1940 | pwlen = strlen(pw); | |
1941 | ||
69cf287c | 1942 | _cups_md5_init(&state); |
452a2203 | 1943 | _cups_md5_append(&state, (unsigned char *)pw, pwlen); |
1944 | _cups_md5_append(&state, (unsigned char *)salt, salt_end - salt); | |
753453e4 | 1945 | |
69cf287c | 1946 | _cups_md5_init(&state2); |
452a2203 | 1947 | _cups_md5_append(&state2, (unsigned char *)pw, pwlen); |
1948 | _cups_md5_append(&state2, (unsigned char *)salt + 3, salt_end - salt - 3); | |
1949 | _cups_md5_append(&state2, (unsigned char *)pw, pwlen); | |
69cf287c | 1950 | _cups_md5_finish(&state2, digest); |
753453e4 | 1951 | |
1952 | for (i = pwlen; i > 0; i -= 16) | |
69cf287c | 1953 | _cups_md5_append(&state, digest, i > 16 ? 16 : i); |
753453e4 | 1954 | |
1955 | for (i = pwlen; i > 0; i >>= 1) | |
452a2203 | 1956 | _cups_md5_append(&state, (unsigned char *)((i & 1) ? "" : pw), 1); |
753453e4 | 1957 | |
69cf287c | 1958 | _cups_md5_finish(&state, digest); |
753453e4 | 1959 | |
1960 | for (i = 0; i < 1000; i ++) | |
1961 | { | |
69cf287c | 1962 | _cups_md5_init(&state); |
753453e4 | 1963 | |
1964 | if (i & 1) | |
452a2203 | 1965 | _cups_md5_append(&state, (unsigned char *)pw, pwlen); |
753453e4 | 1966 | else |
69cf287c | 1967 | _cups_md5_append(&state, digest, 16); |
753453e4 | 1968 | |
1969 | if (i % 3) | |
452a2203 | 1970 | _cups_md5_append(&state, (unsigned char *)salt + 3, salt_end - salt - 3); |
753453e4 | 1971 | |
1972 | if (i % 7) | |
452a2203 | 1973 | _cups_md5_append(&state, (unsigned char *)pw, pwlen); |
753453e4 | 1974 | |
1975 | if (i & 1) | |
69cf287c | 1976 | _cups_md5_append(&state, digest, 16); |
753453e4 | 1977 | else |
452a2203 | 1978 | _cups_md5_append(&state, (unsigned char *)pw, pwlen); |
753453e4 | 1979 | |
69cf287c | 1980 | _cups_md5_finish(&state, digest); |
753453e4 | 1981 | } |
1982 | ||
1983 | /* | |
1984 | * Copy the final sum to the result string and return... | |
1985 | */ | |
1986 | ||
1987 | memcpy(result, salt, salt_end - salt); | |
1988 | ptr = result + (salt_end - salt); | |
1989 | *ptr++ = '$'; | |
1990 | ||
1991 | for (i = 0; i < 5; i ++, ptr += 4) | |
1992 | { | |
1993 | n = (((digest[i] << 8) | digest[i + 6]) << 8); | |
1994 | ||
1995 | if (i < 4) | |
1996 | n |= digest[i + 12]; | |
1997 | else | |
1998 | n |= digest[5]; | |
1999 | ||
2000 | to64(ptr, n, 4); | |
2001 | } | |
2002 | ||
2003 | to64(ptr, digest[11], 2); | |
2004 | ptr += 2; | |
2005 | *ptr = '\0'; | |
2006 | ||
2007 | return (result); | |
2008 | } | |
2009 | else | |
2010 | { | |
2011 | /* | |
2012 | * Use the standard crypt() function... | |
2013 | */ | |
2014 | ||
2015 | return (crypt(pw, salt)); | |
2016 | } | |
2017 | } | |
2018 | #endif /* !HAVE_LIBPAM */ | |
2019 | ||
2020 | ||
a6239644 | 2021 | #if HAVE_LIBPAM |
664de97a | 2022 | /* |
2023 | * 'pam_func()' - PAM conversation function. | |
2024 | */ | |
2025 | ||
f3e786fc | 2026 | static int /* O - Success or failure */ |
2027 | pam_func( | |
2028 | int num_msg, /* I - Number of messages */ | |
2029 | const struct pam_message **msg, /* I - Messages */ | |
2030 | struct pam_response **resp, /* O - Responses */ | |
2031 | void *appdata_ptr) | |
2032 | /* I - Pointer to connection */ | |
664de97a | 2033 | { |
f3e786fc | 2034 | int i; /* Looping var */ |
2035 | struct pam_response *replies; /* Replies */ | |
ff84728d | 2036 | cupsd_authdata_t *data; /* Pointer to auth data */ |
664de97a | 2037 | |
2038 | ||
2039 | /* | |
2040 | * Allocate memory for the responses... | |
2041 | */ | |
2042 | ||
2043 | if ((replies = malloc(sizeof(struct pam_response) * num_msg)) == NULL) | |
2044 | return (PAM_CONV_ERR); | |
2045 | ||
2046 | /* | |
2047 | * Answer all of the messages... | |
2048 | */ | |
2049 | ||
b5cb0608 | 2050 | DEBUG_printf(("pam_func: appdata_ptr = %p\n", appdata_ptr)); |
2051 | ||
2052 | #ifdef __hpux | |
2053 | /* | |
2054 | * Apparently some versions of HP-UX 11 have a broken pam_unix security | |
2055 | * module. This is a workaround... | |
2056 | */ | |
2057 | ||
ff84728d | 2058 | data = auth_data; |
b5cb0608 | 2059 | (void)appdata_ptr; |
2060 | #else | |
ff84728d | 2061 | data = (cupsd_authdata_t *)appdata_ptr; |
b5cb0608 | 2062 | #endif /* __hpux */ |
664de97a | 2063 | |
2064 | for (i = 0; i < num_msg; i ++) | |
b5cb0608 | 2065 | { |
2066 | DEBUG_printf(("pam_func: Message = \"%s\"\n", msg[i]->msg)); | |
2067 | ||
664de97a | 2068 | switch (msg[i]->msg_style) |
2069 | { | |
2070 | case PAM_PROMPT_ECHO_ON: | |
b5cb0608 | 2071 | DEBUG_printf(("pam_func: PAM_PROMPT_ECHO_ON, returning \"%s\"...\n", |
ff84728d | 2072 | data->username)); |
664de97a | 2073 | replies[i].resp_retcode = PAM_SUCCESS; |
ff84728d | 2074 | replies[i].resp = strdup(data->username); |
664de97a | 2075 | break; |
2076 | ||
2077 | case PAM_PROMPT_ECHO_OFF: | |
b5cb0608 | 2078 | DEBUG_printf(("pam_func: PAM_PROMPT_ECHO_OFF, returning \"%s\"...\n", |
ff84728d | 2079 | data->password)); |
664de97a | 2080 | replies[i].resp_retcode = PAM_SUCCESS; |
ff84728d | 2081 | replies[i].resp = strdup(data->password); |
664de97a | 2082 | break; |
2083 | ||
2084 | case PAM_TEXT_INFO: | |
b5cb0608 | 2085 | DEBUG_puts("pam_func: PAM_TEXT_INFO..."); |
2086 | replies[i].resp_retcode = PAM_SUCCESS; | |
2087 | replies[i].resp = NULL; | |
2088 | break; | |
2089 | ||
664de97a | 2090 | case PAM_ERROR_MSG: |
b5cb0608 | 2091 | DEBUG_puts("pam_func: PAM_ERROR_MSG..."); |
664de97a | 2092 | replies[i].resp_retcode = PAM_SUCCESS; |
2093 | replies[i].resp = NULL; | |
2094 | break; | |
2095 | ||
2096 | default: | |
b5cb0608 | 2097 | DEBUG_printf(("pam_func: Unknown PAM message %d...\n", |
2098 | msg[i]->msg_style)); | |
664de97a | 2099 | free(replies); |
2100 | return (PAM_CONV_ERR); | |
2101 | } | |
b5cb0608 | 2102 | } |
664de97a | 2103 | |
2104 | /* | |
2105 | * Return the responses back to PAM... | |
2106 | */ | |
2107 | ||
2108 | *resp = replies; | |
2109 | ||
2110 | return (PAM_SUCCESS); | |
2111 | } | |
753453e4 | 2112 | #else |
2113 | ||
2114 | ||
2115 | /* | |
2116 | * 'to64()' - Base64-encode an integer value... | |
2117 | */ | |
2118 | ||
2119 | static void | |
f3e786fc | 2120 | to64(char *s, /* O - Output string */ |
2121 | unsigned long v, /* I - Value to encode */ | |
2122 | int n) /* I - Number of digits */ | |
753453e4 | 2123 | { |
2124 | const char *itoa64 = "./0123456789" | |
2125 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | |
2126 | "abcdefghijklmnopqrstuvwxyz"; | |
2127 | ||
2128 | ||
2129 | for (; n > 0; n --, v >>= 6) | |
2130 | *s++ = itoa64[v & 0x3f]; | |
2131 | } | |
664de97a | 2132 | #endif /* HAVE_LIBPAM */ |
2133 | ||
2134 | ||
824bac0b | 2135 | /* |
b2e10895 | 2136 | * End of "$Id$". |
824bac0b | 2137 | */ |