1 /* $OpenBSD: sel_subs.c,v 1.20 2009/11/12 20:17:03 deraadt Exp $ */ 2 /* $NetBSD: sel_subs.c,v 1.5 1995/03/21 09:07:42 cgd Exp $ */ 3 4 /*- 5 * Copyright (c) 1992 Keith Muller. 6 * Copyright (c) 1992, 1993 7 * The Regents of the University of California. All rights reserved. 8 * 9 * This code is derived from software contributed to Berkeley by 10 * Keith Muller of the University of California, San Diego. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 3. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 */ 36 37 #include <sys/types.h> 38 #include <sys/time.h> 39 #include <sys/stat.h> 40 #include <sys/param.h> 41 #include <ctype.h> 42 #include <grp.h> 43 #include <pwd.h> 44 #include <stdio.h> 45 #include <stdlib.h> 46 #include <string.h> 47 #include <tzfile.h> 48 #include <unistd.h> 49 #include "pax.h" 50 #include "sel_subs.h" 51 #include "extern.h" 52 53 static int str_sec(const char *, time_t *); 54 static int usr_match(ARCHD *); 55 static int grp_match(ARCHD *); 56 static int trng_match(ARCHD *); 57 58 static TIME_RNG *trhead = NULL; /* time range list head */ 59 static TIME_RNG *trtail = NULL; /* time range list tail */ 60 static USRT **usrtb = NULL; /* user selection table */ 61 static GRPT **grptb = NULL; /* group selection table */ 62 63 /* 64 * Routines for selection of archive members 65 */ 66 67 /* 68 * sel_chk() 69 * check if this file matches a specified uid, gid or time range 70 * Return: 71 * 0 if this archive member should be processed, 1 if it should be skipped 72 */ 73 74 int 75 sel_chk(ARCHD *arcn) 76 { 77 if (((usrtb != NULL) && usr_match(arcn)) || 78 ((grptb != NULL) && grp_match(arcn)) || 79 ((trhead != NULL) && trng_match(arcn))) 80 return(1); 81 return(0); 82 } 83 84 /* 85 * User/group selection routines 86 * 87 * Routines to handle user selection of files based on the file uid/gid. To 88 * add an entry, the user supplies either the name or the uid/gid starting with 89 * a # on the command line. A \# will escape the #. 90 */ 91 92 /* 93 * usr_add() 94 * add a user match to the user match hash table 95 * Return: 96 * 0 if added ok, -1 otherwise; 97 */ 98 99 int 100 usr_add(char *str) 101 { 102 u_int indx; 103 USRT *pt; 104 struct passwd *pw; 105 uid_t uid; 106 107 /* 108 * create the table if it doesn't exist 109 */ 110 if ((str == NULL) || (*str == '\0')) 111 return(-1); 112 if ((usrtb == NULL) && 113 ((usrtb = (USRT **)calloc(USR_TB_SZ, sizeof(USRT *))) == NULL)) { 114 paxwarn(1, "Unable to allocate memory for user selection table"); 115 return(-1); 116 } 117 118 /* 119 * figure out user spec 120 */ 121 if (str[0] != '#') { 122 /* 123 * it is a user name, \# escapes # as first char in user name 124 */ 125 if ((str[0] == '\\') && (str[1] == '#')) 126 ++str; 127 if ((pw = getpwnam(str)) == NULL) { 128 paxwarn(1, "Unable to find uid for user: %s", str); 129 return(-1); 130 } 131 uid = (uid_t)pw->pw_uid; 132 } else 133 uid = (uid_t)strtoul(str+1, NULL, 10); 134 endpwent(); 135 136 /* 137 * hash it and go down the hash chain (if any) looking for it 138 */ 139 indx = ((unsigned)uid) % USR_TB_SZ; 140 if ((pt = usrtb[indx]) != NULL) { 141 while (pt != NULL) { 142 if (pt->uid == uid) 143 return(0); 144 pt = pt->fow; 145 } 146 } 147 148 /* 149 * uid is not yet in the table, add it to the front of the chain 150 */ 151 if ((pt = (USRT *)malloc(sizeof(USRT))) != NULL) { 152 pt->uid = uid; 153 pt->fow = usrtb[indx]; 154 usrtb[indx] = pt; 155 return(0); 156 } 157 paxwarn(1, "User selection table out of memory"); 158 return(-1); 159 } 160 161 /* 162 * usr_match() 163 * check if this files uid matches a selected uid. 164 * Return: 165 * 0 if this archive member should be processed, 1 if it should be skipped 166 */ 167 168 static int 169 usr_match(ARCHD *arcn) 170 { 171 USRT *pt; 172 173 /* 174 * hash and look for it in the table 175 */ 176 pt = usrtb[((unsigned)arcn->sb.st_uid) % USR_TB_SZ]; 177 while (pt != NULL) { 178 if (pt->uid == arcn->sb.st_uid) 179 return(0); 180 pt = pt->fow; 181 } 182 183 /* 184 * not found 185 */ 186 return(1); 187 } 188 189 /* 190 * grp_add() 191 * add a group match to the group match hash table 192 * Return: 193 * 0 if added ok, -1 otherwise; 194 */ 195 196 int 197 grp_add(char *str) 198 { 199 u_int indx; 200 GRPT *pt; 201 struct group *gr; 202 gid_t gid; 203 204 /* 205 * create the table if it doesn't exist 206 */ 207 if ((str == NULL) || (*str == '\0')) 208 return(-1); 209 if ((grptb == NULL) && 210 ((grptb = (GRPT **)calloc(GRP_TB_SZ, sizeof(GRPT *))) == NULL)) { 211 paxwarn(1, "Unable to allocate memory fo group selection table"); 212 return(-1); 213 } 214 215 /* 216 * figure out user spec 217 */ 218 if (str[0] != '#') { 219 /* 220 * it is a group name, \# escapes # as first char in group name 221 */ 222 if ((str[0] == '\\') && (str[1] == '#')) 223 ++str; 224 if ((gr = getgrnam(str)) == NULL) { 225 paxwarn(1,"Cannot determine gid for group name: %s", str); 226 return(-1); 227 } 228 gid = (gid_t)gr->gr_gid; 229 } else 230 gid = (gid_t)strtoul(str+1, NULL, 10); 231 endgrent(); 232 233 /* 234 * hash it and go down the hash chain (if any) looking for it 235 */ 236 indx = ((unsigned)gid) % GRP_TB_SZ; 237 if ((pt = grptb[indx]) != NULL) { 238 while (pt != NULL) { 239 if (pt->gid == gid) 240 return(0); 241 pt = pt->fow; 242 } 243 } 244 245 /* 246 * gid not in the table, add it to the front of the chain 247 */ 248 if ((pt = (GRPT *)malloc(sizeof(GRPT))) != NULL) { 249 pt->gid = gid; 250 pt->fow = grptb[indx]; 251 grptb[indx] = pt; 252 return(0); 253 } 254 paxwarn(1, "Group selection table out of memory"); 255 return(-1); 256 } 257 258 /* 259 * grp_match() 260 * check if this files gid matches a selected gid. 261 * Return: 262 * 0 if this archive member should be processed, 1 if it should be skipped 263 */ 264 265 static int 266 grp_match(ARCHD *arcn) 267 { 268 GRPT *pt; 269 270 /* 271 * hash and look for it in the table 272 */ 273 pt = grptb[((unsigned)arcn->sb.st_gid) % GRP_TB_SZ]; 274 while (pt != NULL) { 275 if (pt->gid == arcn->sb.st_gid) 276 return(0); 277 pt = pt->fow; 278 } 279 280 /* 281 * not found 282 */ 283 return(1); 284 } 285 286 /* 287 * Time range selection routines 288 * 289 * Routines to handle user selection of files based on the modification and/or 290 * inode change time falling within a specified time range (the non-standard 291 * -T flag). The user may specify any number of different file time ranges. 292 * Time ranges are checked one at a time until a match is found (if at all). 293 * If the file has a mtime (and/or ctime) which lies within one of the time 294 * ranges, the file is selected. Time ranges may have a lower and/or a upper 295 * value. These ranges are inclusive. When no time ranges are supplied to pax 296 * with the -T option, all members in the archive will be selected by the time 297 * range routines. When only a lower range is supplied, only files with a 298 * mtime (and/or ctime) equal to or younger are selected. When only a upper 299 * range is supplied, only files with a mtime (and/or ctime) equal to or older 300 * are selected. When the lower time range is equal to the upper time range, 301 * only files with a mtime (or ctime) of exactly that time are selected. 302 */ 303 304 /* 305 * trng_add() 306 * add a time range match to the time range list. 307 * This is a non-standard pax option. Lower and upper ranges are in the 308 * format: [[[[[cc]yy]mm]dd]HH]MM[.SS] and are comma separated. 309 * Time ranges are based on current time, so 1234 would specify a time of 310 * 12:34 today. 311 * Return: 312 * 0 if the time range was added to the list, -1 otherwise 313 */ 314 315 int 316 trng_add(char *str) 317 { 318 TIME_RNG *pt; 319 char *up_pt = NULL; 320 char *stpt; 321 char *flgpt; 322 int dot = 0; 323 324 /* 325 * throw out the badly formed time ranges 326 */ 327 if ((str == NULL) || (*str == '\0')) { 328 paxwarn(1, "Empty time range string"); 329 return(-1); 330 } 331 332 /* 333 * locate optional flags suffix /{cm}. 334 */ 335 if ((flgpt = strrchr(str, '/')) != NULL) 336 *flgpt++ = '\0'; 337 338 for (stpt = str; *stpt != '\0'; ++stpt) { 339 if ((*stpt >= '0') && (*stpt <= '9')) 340 continue; 341 if ((*stpt == ',') && (up_pt == NULL)) { 342 *stpt = '\0'; 343 up_pt = stpt + 1; 344 dot = 0; 345 continue; 346 } 347 348 /* 349 * allow only one dot per range (secs) 350 */ 351 if ((*stpt == '.') && (!dot)) { 352 ++dot; 353 continue; 354 } 355 paxwarn(1, "Improperly specified time range: %s", str); 356 goto out; 357 } 358 359 /* 360 * allocate space for the time range and store the limits 361 */ 362 if ((pt = (TIME_RNG *)malloc(sizeof(TIME_RNG))) == NULL) { 363 paxwarn(1, "Unable to allocate memory for time range"); 364 return(-1); 365 } 366 367 /* 368 * by default we only will check file mtime, but user can specify 369 * mtime, ctime (inode change time) or both. 370 */ 371 if ((flgpt == NULL) || (*flgpt == '\0')) 372 pt->flgs = CMPMTME; 373 else { 374 pt->flgs = 0; 375 while (*flgpt != '\0') { 376 switch (*flgpt) { 377 case 'M': 378 case 'm': 379 pt->flgs |= CMPMTME; 380 break; 381 case 'C': 382 case 'c': 383 pt->flgs |= CMPCTME; 384 break; 385 default: 386 paxwarn(1, "Bad option %c with time range %s", 387 *flgpt, str); 388 (void)free((char *)pt); 389 goto out; 390 } 391 ++flgpt; 392 } 393 } 394 395 /* 396 * start off with the current time 397 */ 398 pt->low_time = pt->high_time = time(NULL); 399 if (*str != '\0') { 400 /* 401 * add lower limit 402 */ 403 if (str_sec(str, &(pt->low_time)) < 0) { 404 paxwarn(1, "Illegal lower time range %s", str); 405 (void)free((char *)pt); 406 goto out; 407 } 408 pt->flgs |= HASLOW; 409 } 410 411 if ((up_pt != NULL) && (*up_pt != '\0')) { 412 /* 413 * add upper limit 414 */ 415 if (str_sec(up_pt, &(pt->high_time)) < 0) { 416 paxwarn(1, "Illegal upper time range %s", up_pt); 417 (void)free((char *)pt); 418 goto out; 419 } 420 pt->flgs |= HASHIGH; 421 422 /* 423 * check that the upper and lower do not overlap 424 */ 425 if (pt->flgs & HASLOW) { 426 if (pt->low_time > pt->high_time) { 427 paxwarn(1, "Upper %s and lower %s time overlap", 428 up_pt, str); 429 (void)free((char *)pt); 430 return(-1); 431 } 432 } 433 } 434 435 pt->fow = NULL; 436 if (trhead == NULL) { 437 trtail = trhead = pt; 438 return(0); 439 } 440 trtail->fow = pt; 441 trtail = pt; 442 return(0); 443 444 out: 445 paxwarn(1, "Time range format is: [[[[[cc]yy]mm]dd]HH]MM[.SS][/[c][m]]"); 446 return(-1); 447 } 448 449 /* 450 * trng_match() 451 * check if this files mtime/ctime falls within any supplied time range. 452 * Return: 453 * 0 if this archive member should be processed, 1 if it should be skipped 454 */ 455 456 static int 457 trng_match(ARCHD *arcn) 458 { 459 TIME_RNG *pt; 460 461 /* 462 * have to search down the list one at a time looking for a match. 463 * remember time range limits are inclusive. 464 */ 465 pt = trhead; 466 while (pt != NULL) { 467 switch (pt->flgs & CMPBOTH) { 468 case CMPBOTH: 469 /* 470 * user wants both mtime and ctime checked for this 471 * time range 472 */ 473 if (((pt->flgs & HASLOW) && 474 (arcn->sb.st_mtime < pt->low_time) && 475 (arcn->sb.st_ctime < pt->low_time)) || 476 ((pt->flgs & HASHIGH) && 477 (arcn->sb.st_mtime > pt->high_time) && 478 (arcn->sb.st_ctime > pt->high_time))) { 479 pt = pt->fow; 480 continue; 481 } 482 break; 483 case CMPCTME: 484 /* 485 * user wants only ctime checked for this time range 486 */ 487 if (((pt->flgs & HASLOW) && 488 (arcn->sb.st_ctime < pt->low_time)) || 489 ((pt->flgs & HASHIGH) && 490 (arcn->sb.st_ctime > pt->high_time))) { 491 pt = pt->fow; 492 continue; 493 } 494 break; 495 case CMPMTME: 496 default: 497 /* 498 * user wants only mtime checked for this time range 499 */ 500 if (((pt->flgs & HASLOW) && 501 (arcn->sb.st_mtime < pt->low_time)) || 502 ((pt->flgs & HASHIGH) && 503 (arcn->sb.st_mtime > pt->high_time))) { 504 pt = pt->fow; 505 continue; 506 } 507 break; 508 } 509 break; 510 } 511 512 if (pt == NULL) 513 return(1); 514 return(0); 515 } 516 517 /* 518 * str_sec() 519 * Convert a time string in the format of [[[[[cc]yy]mm]dd]HH]MM[.SS] to 520 * seconds UTC. Tval already has current time loaded into it at entry. 521 * Return: 522 * 0 if converted ok, -1 otherwise 523 */ 524 525 static int 526 str_sec(const char *p, time_t *tval) 527 { 528 struct tm *lt; 529 const char *dot, *t; 530 size_t len; 531 int bigyear; 532 int yearset; 533 534 yearset = 0; 535 len = strlen(p); 536 537 for (t = p, dot = NULL; *t; ++t) { 538 if (isdigit(*t)) 539 continue; 540 if (*t == '.' && dot == NULL) { 541 dot = t; 542 continue; 543 } 544 return(-1); 545 } 546 547 lt = localtime(tval); 548 549 if (dot != NULL) { /* .SS */ 550 if (strlen(++dot) != 2) 551 return(-1); 552 lt->tm_sec = ATOI2(dot); 553 if (lt->tm_sec > 61) 554 return(-1); 555 len -= 3; 556 } else 557 lt->tm_sec = 0; 558 559 switch (len) { 560 case 12: /* cc */ 561 bigyear = ATOI2(p); 562 lt->tm_year = (bigyear * 100) - TM_YEAR_BASE; 563 yearset = 1; 564 /* FALLTHROUGH */ 565 case 10: /* yy */ 566 if (yearset) { 567 lt->tm_year += ATOI2(p); 568 } else { 569 lt->tm_year = ATOI2(p); 570 if (lt->tm_year < 69) /* hack for 2000 ;-} */ 571 lt->tm_year += (2000 - TM_YEAR_BASE); 572 else 573 lt->tm_year += (1900 - TM_YEAR_BASE); 574 } 575 /* FALLTHROUGH */ 576 case 8: /* mm */ 577 lt->tm_mon = ATOI2(p); 578 if ((lt->tm_mon > 12) || !lt->tm_mon) 579 return(-1); 580 --lt->tm_mon; /* time struct is 0 - 11 */ 581 /* FALLTHROUGH */ 582 case 6: /* dd */ 583 lt->tm_mday = ATOI2(p); 584 if ((lt->tm_mday > 31) || !lt->tm_mday) 585 return(-1); 586 /* FALLTHROUGH */ 587 case 4: /* HH */ 588 lt->tm_hour = ATOI2(p); 589 if (lt->tm_hour > 23) 590 return(-1); 591 /* FALLTHROUGH */ 592 case 2: /* MM */ 593 lt->tm_min = ATOI2(p); 594 if (lt->tm_min > 59) 595 return(-1); 596 break; 597 default: 598 return(-1); 599 } 600 601 /* convert broken-down time to UTC clock time seconds */ 602 if ((*tval = mktime(lt)) == -1) 603 return(-1); 604 return(0); 605 } 606