UNIXworkcode

1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright 2013 Olaf Wintermann. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <unistd.h> 32 33 #include "../util/util.h" 34 #include "../util/pool.h" 35 #include "../util/pblock.h" 36 #include "../safs/auth.h" 37 #include "log.h" 38 #include "acl.h" 39 40 #define AUTH_TYPE_BASIC "basic" 41 42 void acllist_createhandle(Session *sn, Request *rq) { 43 ACLListHandle *handle = pool_malloc(sn->pool, sizeof(ACLListHandle)); 44 handle->defaultauthdb = NULL; 45 handle->listhead = NULL; 46 handle->listtail = NULL; 47 rq->acllist = handle; 48 } 49 50 /* 51 * append or prepend an ACL 52 */ 53 void acllist_add(Session *sn, Request *rq, ACLList *acl, int append) { 54 if(!rq->acllist) { 55 acllist_createhandle(sn, rq); 56 } 57 ACLListHandle *list = rq->acllist; 58 59 if(!list->defaultauthdb && acl->authdb) { 60 list->defaultauthdb = acl->authdb; 61 } 62 63 ACLListElm *elm = pool_malloc(sn->pool, sizeof(ACLListElm)); 64 elm->acl = acl; 65 elm->next = NULL; 66 if(list->listhead == NULL) { 67 list->listhead = elm; 68 list->listtail = elm; 69 } else { 70 if(append) { 71 list->listtail->next = elm; 72 list->listtail = elm; 73 } else { 74 elm->next = list->listhead; 75 list->listhead = elm; 76 } 77 } 78 } 79 80 void acllist_append(Session *sn, Request *rq, ACLList *acl) { 81 acllist_add(sn, rq, acl, 1); 82 } 83 84 void acllist_prepend(Session *sn, Request *rq, ACLList *acl) { 85 acllist_add(sn, rq, acl, 0); 86 } 87 88 uint32_t acl_oflag2mask(int oflags) { 89 /* TODO: 90 * maybe there is a plattform where O_RDWR is not O_RDONLY | O_WRONLY 91 */ 92 uint32_t access_mask = 0; 93 if((oflags & O_RDONLY) == O_RDONLY) { 94 access_mask |= ACL_READ_DATA; 95 } 96 if((oflags & O_WRONLY) == O_WRONLY) { 97 access_mask |= ACL_WRITE_DATA; 98 } 99 return access_mask; 100 } 101 102 User* acllist_getuser(Session *sn, Request *rq, ACLListHandle *list) { 103 // TODO: cache result #50 104 if(!sn || !rq || !list) { 105 return NULL; 106 } 107 108 // get user 109 User *user = NULL; 110 if(list->defaultauthdb) { 111 char *usr; 112 char *pw; 113 if(!basicauth_getuser(sn, rq, &usr, &pw)) { 114 int pwok; 115 user = authdb_get_and_verify(list->defaultauthdb, sn, rq, usr, pw, &pwok); 116 if(!user) { 117 // wrong user or wrong password 118 return NULL; 119 } 120 // ok - user is authenticated 121 pblock_kvinsert( 122 pb_key_auth_user, 123 user->name, 124 strlen(user->name), 125 rq->vars); 126 pblock_kvinsert( 127 pb_key_auth_type, 128 AUTH_TYPE_BASIC, 129 sizeof(AUTH_TYPE_BASIC)-1, 130 rq->vars); 131 } 132 } 133 134 return user; 135 } 136 137 void acl_set_error_status(Session *sn, Request *rq, ACLList *acl, User *user) { 138 if(sn == NULL || rq == NULL) { 139 return; 140 } 141 142 if(!user) { 143 char *value = NULL; 144 if(acl->authprompt) { 145 size_t realmlen = strlen(acl->authprompt); 146 size_t len = realmlen + 16; 147 value = pool_malloc(sn->pool, len); 148 if(value) { 149 snprintf( 150 value, 151 len, 152 "Basic realm=\"%s\"", 153 acl->authprompt); 154 } 155 } 156 if(!value) { 157 value = "Basic realm=\"login\""; 158 } 159 pblock_nvinsert("www-authenticate", value, rq->srvhdrs); 160 protocol_status(sn, rq, PROTOCOL_UNAUTHORIZED, NULL); 161 } else { 162 protocol_status(sn, rq, PROTOCOL_FORBIDDEN, NULL); 163 } 164 } 165 166 int acl_evaluate(Session *sn, Request *rq, int access_mask) { 167 ACLListHandle *list = rq->acllist; 168 if(!list) { 169 return REQ_PROCEED; 170 } 171 172 // we combine access_mask with the required access rights 173 access_mask |= rq->aclreqaccess; 174 175 // get user 176 User *user = acllist_getuser(sn, rq, list); 177 178 // evalutate all ACLs 179 ACLList *acl = acl_evallist(list, user, access_mask, NULL); 180 if(acl) { 181 acl_set_error_status(sn, rq, acl, user); 182 return REQ_ABORTED; 183 } 184 185 return REQ_PROCEED; 186 } 187 188 ACLList* acl_evallist( 189 ACLListHandle *list, 190 User *user, 191 int access_mask, 192 ACLList **externacl) 193 { 194 if(!list) { 195 return NULL; 196 } 197 if(externacl) { 198 *externacl = NULL; 199 } 200 201 // evaluate each acl until one denies access 202 ACLListElm *elm = list->listhead; 203 while(elm) { 204 ACLList *acl = elm->acl; 205 if(acl->isextern) { 206 // set externacl to the first external acl 207 if(externacl && *externacl == NULL) { 208 *externacl = acl; 209 } 210 } else if(!acl->check(acl, user, access_mask)) { 211 // the acl denies access 212 return acl; 213 } 214 elm = elm->next; 215 } 216 217 // ok - all acls allowed access 218 219 return NULL; 220 } 221 222 int wsacl_affects_user(WSAce *ace, User *user) { 223 int check_access = 0; 224 225 /* 226 * an ace can affect 227 * a named user or group (ace->who is set) 228 * the owner of the resource (ACL_OWNER is set) 229 * the owning group of the resource (ACL_GROUP is set) 230 * everyone (ACL_EVERYONE is set) 231 * 232 * Only one of this conditions should be true. The behavior on 233 * illegal flag combination is undefined. We assume that the acls 234 * are created correctly by the configuration loader. 235 */ 236 237 if(ace->who && user) { 238 // this ace is defined for a named user or group 239 if((ace->flags & ACL_IDENTIFIER_GROUP) == ACL_IDENTIFIER_GROUP) { 240 if(user->check_group(user, ace->who)) { 241 // the user is in the group 242 check_access = 1; 243 } 244 } else { 245 if(!strcmp(user->name, ace->who)) { 246 check_access = 1; 247 } 248 } 249 } else if((ace->flags & ACL_OWNER) == ACL_OWNER) { 250 // TODO 251 } else if((ace->flags & ACL_GROUP) == ACL_GROUP) { 252 // TODO 253 } else if((ace->flags & ACL_EVERYONE) == ACL_EVERYONE) { 254 check_access = 1; 255 } 256 257 return check_access; 258 } 259 260 int wsacl_check(WSAcl *acl, User *user, int access_mask) { 261 int allow = 0; 262 uint32_t allowed_access = 0; 263 // check each access control entry 264 for(int i=0;i<acl->acenum;i++) { 265 WSAce *ace = acl->ace[i]; 266 if(wsacl_affects_user(ace, user)) { 267 if(ace->type == ACL_TYPE_ALLOWED) { 268 // add all new access rights 269 allowed_access |= (access_mask & ace->access_mask); 270 // check if we have all requested rights 271 if((allowed_access & access_mask) == access_mask) { 272 allow = 1; 273 break; 274 } 275 } else { 276 // ACL_TYPE_DENIED 277 278 if((ace->access_mask & access_mask) != 0) { 279 // access denied 280 break; 281 } 282 } 283 } 284 } 285 286 // TODO: events 287 288 return allow; // allow is 0, if no ace set it to 1 289 } 290 291 292 /* filesystem acl functions */ 293 294 #if defined (__SVR4) && defined (__sun) 295 296 #include <sys/acl.h> 297 298 int solaris_acl_check( 299 char *path, 300 struct stat *s, 301 uint32_t mask, 302 uid_t uid, 303 gid_t gid); 304 int solaris_acl_affects_user( 305 ace_t *ace, 306 uid_t uid, 307 gid_t gid, 308 uid_t owner, 309 gid_t owninggroup); 310 311 int fs_acl_check(SysACL *acl, User *user, const char *path, uint32_t access_mask) { 312 cxmutstr p; 313 if(path[0] != '/') { 314 size_t n = 128; 315 char *cwd = malloc(n); 316 while(!getcwd(cwd, n)) { 317 if(errno == ERANGE) { 318 n *= 2; 319 cwd = realloc(cwd, n); 320 } else { 321 free(cwd); 322 return 0; 323 } 324 } 325 cxmutstr wd = cx_str(cwd); 326 cxmutstr pp = cx_str((char*)path); 327 328 p = cx_strcat(3, wd, cx_strn("/", 1), pp); 329 } else { 330 p = cx_strdup(cx_str((char*)path)); 331 } 332 if(p.ptr[p.length-1] == '/') { 333 p.ptr[p.length-1] = 0; 334 p.length--; 335 } 336 337 // get uid/gid 338 struct passwd pw; 339 if(user) { 340 char *pwbuf = malloc(DEF_PWBUF); 341 if(pwbuf == NULL) { 342 free(p.ptr); 343 return 0; 344 } 345 if(!util_getpwnam(user->name, &pw, pwbuf, DEF_PWBUF)) { 346 free(pwbuf); 347 free(p.ptr); 348 return 0; 349 } 350 free(pwbuf); 351 acl->user_uid = pw.pw_uid; 352 acl->user_gid = pw.pw_gid; 353 } else { 354 acl->user_uid = -1; 355 acl->user_gid = -1; 356 } 357 358 // translate access_mask 359 uint32_t mask = 0; 360 if((access_mask & ACL_READ_DATA) == ACL_READ_DATA) { 361 mask |= ACE_READ_DATA; 362 } 363 if((access_mask & ACL_WRITE_DATA) == ACL_WRITE_DATA) { 364 mask |= ACE_WRITE_DATA; 365 } 366 if((access_mask & ACL_ADD_FILE) == ACL_ADD_FILE) { 367 mask |= ACE_ADD_FILE; 368 } 369 if((access_mask & ACL_READ_XATTR) == ACL_READ_XATTR) { 370 mask |= ACE_READ_NAMED_ATTRS; 371 } 372 if((access_mask & ACL_WRITE_XATTR) == ACL_WRITE_XATTR) { 373 mask |= ACE_WRITE_NAMED_ATTRS; 374 } 375 if((access_mask & ACL_EXECUTE) == ACL_EXECUTE) { 376 mask |= ACE_EXECUTE; 377 } 378 if((access_mask & ACL_DELETE) == ACL_DELETE) { 379 mask |= ACE_DELETE_CHILD; 380 } 381 if((access_mask & ACL_READ_ATTRIBUTES) == ACL_READ_ATTRIBUTES) { 382 mask |= ACE_READ_ATTRIBUTES; 383 } 384 if((access_mask & ACL_WRITE_ATTRIBUTES) == ACL_WRITE_ATTRIBUTES) { 385 mask |= ACE_WRITE_ATTRIBUTES; 386 } 387 if((access_mask & ACL_LIST) == ACL_LIST) { 388 mask |= ACE_LIST_DIRECTORY; 389 } 390 if((access_mask & ACL_READ_ACL) == ACL_READ_ACL) { 391 mask |= ACE_READ_ACL; 392 } 393 if((access_mask & ACL_WRITE_ACL) == ACL_WRITE_ACL) { 394 mask |= ACE_WRITE_ACL; 395 } 396 if((access_mask & ACL_WRITE_OWNER) == ACL_WRITE_OWNER) { 397 mask |= ACE_WRITE_OWNER; 398 } 399 if((access_mask & ACL_SYNCHRONIZE) == ACL_SYNCHRONIZE) { 400 mask |= ACE_SYNCHRONIZE; 401 } 402 403 /* 404 * If the vfs wants to create new files, path does not name an existing 405 * file. In this case, we check if the user has the ACE_ADD_FILE 406 * permission for the parent directory 407 */ 408 struct stat s; 409 if(stat(p.ptr, &s)) { 410 if(errno != ENOENT) { 411 perror("fs_acl_check: stat"); 412 free(p.ptr); 413 return 0; 414 } else { 415 mask = ACE_ADD_FILE; 416 p = util_path_remove_last(p); 417 if(stat(p.ptr, &s)) { 418 free(p.ptr); 419 return 0; 420 } 421 } 422 } 423 424 /* 425 * perform a acl check for the path and each parent directory 426 * we don't check the file system root 427 * 428 * after the first check, we check only search permission for the 429 * directories 430 */ 431 if(!solaris_acl_check(p.ptr, &s, mask, pw.pw_uid, pw.pw_gid)) { 432 free(p.ptr); 433 return 0; 434 } 435 436 p = util_path_remove_last(p); 437 mask = ACE_LIST_DIRECTORY; 438 while(p.length > 1) { 439 if(stat(p.ptr, &s)) { 440 free(p.ptr); 441 return 0; 442 } 443 if(!solaris_acl_check(p.ptr, &s, mask, pw.pw_uid, pw.pw_gid)) { 444 free(p.ptr); 445 return 0; 446 } 447 448 // cut the last file name from the path 449 p = util_path_remove_last(p); 450 } 451 452 453 return 1; 454 } 455 456 int fs_acl_check_fd(SysACL *acl, User *user, int fd, uint32_t access_mask) { 457 // TODO: 458 return 1; 459 } 460 461 int solaris_acl_check( 462 char *path, 463 struct stat *s, 464 uint32_t mask, 465 uid_t uid, 466 gid_t gid) 467 { 468 //printf("solaris_acl_check %s\n", path); 469 470 int nace = acl(path, ACE_GETACLCNT, 0, NULL); 471 if(nace == -1) { 472 perror("acl: ACE_GETACLCNT"); 473 // TODO: log error 474 return 0; 475 } 476 ace_t *aces = calloc(nace, sizeof(ace_t)); 477 if(acl(path, ACE_GETACL, nace, aces) == 1) { 478 perror("acl: ACE_GETACL"); 479 // TODO: log error 480 free(aces); 481 return 0; 482 } 483 484 int allow = 0; 485 uint32_t allowed_access = 0; 486 for(int i=0;i<nace;i++) { 487 ace_t ace = aces[i]; 488 if(solaris_acl_affects_user(&ace, uid, gid, s->st_uid, s->st_gid)) { 489 if(ace.a_type == ACE_ACCESS_ALLOWED_ACE_TYPE) { 490 // add all new access rights 491 allowed_access |= (mask & ace.a_access_mask); 492 // check if we have all requested rights 493 if((allowed_access & mask) == mask) { 494 allow = 1; 495 break; 496 } 497 } else if(ace.a_type == ACE_ACCESS_DENIED_ACE_TYPE) { 498 // ACL_TYPE_DENIED 499 500 if((ace.a_access_mask & mask) != 0) { 501 // access denied 502 break; 503 } 504 } 505 } 506 } 507 508 free(aces); 509 510 //printf("return %d\n", allow); 511 return allow; 512 } 513 514 int solaris_acl_affects_user( 515 ace_t *ace, 516 uid_t uid, 517 gid_t gid, 518 uid_t owner, 519 gid_t owninggroup) 520 { 521 /* 522 * mostly the same as wsacl_affects_user 523 */ 524 525 int check_access = 0; 526 527 if((ace->a_flags & ACE_OWNER) == ACE_OWNER) { 528 if(uid == owner) { 529 check_access = 1; 530 } 531 } else if((ace->a_flags & ACE_GROUP) == ACE_GROUP) { 532 if(gid == owninggroup) { 533 check_access = 1; 534 } 535 } else if((ace->a_flags & ACE_EVERYONE) == ACE_EVERYONE) { 536 check_access = 1; 537 } else if(ace->a_who != -1 && uid != 0) { 538 // this ace is defined for a named user or group 539 if((ace->a_flags & ACE_IDENTIFIER_GROUP) == ACE_IDENTIFIER_GROUP) { 540 // TODO: check all groups 541 if(ace->a_who == gid) { 542 // the user is in the group 543 check_access = 1; 544 } 545 } else { 546 if(ace->a_who == uid) { 547 check_access = 1; 548 } 549 } 550 } 551 552 return check_access; 553 } 554 555 void fs_acl_finish() { 556 557 } 558 559 #endif 560 561 /* 562 * generic code for all non acl unices 563 * TODO: don't use OSX in the preprocessor directive 564 */ 565 #ifdef OSX 566 567 int fs_acl_check(SysACL *acl, User *user, const char *path, uint32_t access_mask) { 568 return 1; 569 } 570 571 int fs_acl_check_fd(SysACL *acl, User *user, int fd, uint32_t access_mask) { 572 return 1; 573 } 574 575 void fs_acl_finish() { 576 577 } 578 579 #endif 580 581 #if defined(BSD) && !defined(OSX) 582 583 int fs_acl_check(SysACL *acl, User *user, const char *path, uint32_t access_mask) { 584 return 1; 585 } 586 587 int fs_acl_check_fd(SysACL *acl, User *user, int fd, uint32_t access_mask) { 588 return 1; 589 } 590 591 void fs_acl_finish() { 592 593 } 594 595 #endif 596 597 598 #ifdef LINUX 599 600 #include <sys/fsuid.h> 601 602 int fs_acl_check(SysACL *acl, User *user, const char *path, uint32_t access_mask) { 603 struct passwd *ws_pw = conf_getglobals()->Vuserpw; 604 if(!ws_pw) { 605 log_ereport(LOG_FAILURE, "fs_acl_check: unknown webserver uid/gid"); 606 return 1; 607 } 608 609 // get uid/gid 610 struct passwd pw; 611 if(user) { 612 char *pwbuf = malloc(DEF_PWBUF); 613 if(pwbuf == NULL) { 614 return 0; 615 } 616 if(!util_getpwnam(user->name, &pw, pwbuf, DEF_PWBUF)) { 617 free(pwbuf); 618 return 0; 619 } 620 free(pwbuf); 621 acl->user_uid = pw.pw_uid; 622 acl->user_gid = pw.pw_gid; 623 } else { 624 acl->user_uid = 0; 625 acl->user_gid = 0; 626 } 627 628 // set fs uid/gid 629 if(acl->user_uid != 0) { 630 if(setfsuid(pw.pw_uid)) { 631 log_ereport( 632 LOG_FAILURE, 633 "Cannot set fsuid to uid: %u", pw.pw_uid); 634 } 635 if(setfsgid(pw.pw_gid)) { 636 log_ereport( 637 LOG_FAILURE, 638 "Cannot set fsgid to gid: %u", pw.pw_gid); 639 } 640 } 641 642 643 return 1; 644 } 645 646 int fs_acl_check_fd(SysACL *acl, User *user, int fd, uint32_t access_mask) { 647 // TODO 648 return 1; 649 } 650 651 void fs_acl_finish() { 652 struct passwd *pw = conf_getglobals()->Vuserpw; 653 if(!pw) { 654 log_ereport( 655 LOG_FAILURE, 656 "global configuration broken (Vuserpw is null)"); 657 return; 658 } 659 if(setfsuid(pw->pw_uid)) { 660 log_ereport( 661 LOG_FAILURE, 662 "Cannot set fsuid back to server uid: %u", pw->pw_uid); 663 } 664 if(setfsgid(pw->pw_gid)) { 665 log_ereport( 666 LOG_FAILURE, 667 "Cannot set fsgid back to server gid: %u", pw->pw_gid); 668 } 669 } 670 671 #endif 672