rpm  4.5
rpmsx.c
Go to the documentation of this file.
1 
4 #include "system.h"
5 
6 #include <rpmlib.h>
7 #include <rpmmacro.h> /* for rpmGetPath() */
8 
9 #define _RPMSX_INTERNAL
10 #include "rpmsx.h"
11 
12 #include "debug.h"
13 
14 /*@access regex_t @*/
15 
16 /*@unchecked@*/
17 int _rpmsx_debug = 0;
18 
23 static void rpmsxSort(rpmsx sx)
24  /*@modifies sx @*/
25 {
26  rpmsxp sxp;
27  int i, j;
28 
29  /* Stable sort for policy regex's and paths. */
30  sxp = xmalloc(sizeof(*sxp) * sx->Count);
31 
32  /* Regex patterns first ... */
33  j = 0;
34  for (i = 0; i < sx->Count; i++) {
35  if (!sx->sxp[i].hasMetaChars)
36  continue;
37  memcpy(sxp + j, sx->sxp + i, sizeof(*sxp));
38  j++;
39  }
40 
41  /* ... then file paths. */
42  for (i = 0; i < sx->Count; i++) {
43  if (sx->sxp[i].hasMetaChars)
44  continue;
45  memcpy(sxp + j, sx->sxp + i, sizeof(*sxp));
46  j++;
47  }
48 
49  sx->sxp = _free(sx->sxp);
50  sx->sxp = sxp;
51 /*@-compdef@*/ /* XXX *(sx->sxp) annotation */
52  return;
53 /*@=compdef@*/
54 }
55 
56 /* Determine if the regular expression specification has any meta characters. */
57 static void rpmsxpHasMetaChars(rpmsxp sxp)
58  /*@modifies sxp @*/
59 {
60  const char * s = sxp->pattern;
61  size_t ns = strlen(s);
62  const char * se = s + ns;
63 
64  sxp->hasMetaChars = 0;
65 
66  /* Look at each character in the RE specification string for a
67  * meta character. Return when any meta character reached. */
68  while (s != se) {
69  switch(*s) {
70  case '.':
71  case '^':
72  case '$':
73  case '?':
74  case '*':
75  case '+':
76  case '|':
77  case '[':
78  case '(':
79  case '{':
80  sxp->hasMetaChars = 1;
81  return;
82  /*@notreached@*/ /*@switchbreak@*/ break;
83  case '\\': /* skip the next character */
84  s++;
85  /*@switchbreak@*/ break;
86  default:
87  /*@switchbreak@*/ break;
88 
89  }
90  s++;
91  }
92  return;
93 }
94 
99 static size_t rpmsxsPStem(const char * const buf)
100  /*@*/
101 {
102  /*@observer@*/
103  static const char * const regex_chars = ".^$?*+|[({";
104  const char * tmp = strchr(buf, '/');
105  const char * ind;
106 
107  if (!tmp)
108  return 0;
109 
110  for (ind = buf; ind < tmp; ind++) {
111  if (strchr(regex_chars, (int)*ind))
112  return 0;
113  }
114  return tmp - buf;
115 }
116 
121 static size_t rpmsxsFStem(const char * const buf)
122  /*@*/
123 {
124  const char * tmp = strchr(buf + 1, '/');
125 
126  if (!tmp)
127  return 0;
128  return tmp - buf;
129 }
130 
138 static int rpmsxAdd(rpmsx sx, const char ** bpp)
139  /*@modifies sx, *bpp @*/
140 {
141  size_t stem_len = rpmsxsPStem(*bpp);
142  rpmsxs sxs;
143  int i;
144 
145  if (!stem_len)
146  return -1;
147  for (i = 0; i < sx->nsxs; i++) {
148  sxs = sx->sxs + i;
149  if (stem_len != sxs->len)
150  continue;
151  if (strncmp(*bpp, sxs->stem, stem_len))
152  continue;
153  *bpp += stem_len;
154  return i;
155  }
156 
157  if (sx->nsxs == sx->maxsxs) {
158  sx->maxsxs = sx->maxsxs * 2 + 16;
159  sx->sxs = xrealloc(sx->sxs, sizeof(*sx->sxs) * sx->maxsxs);
160  }
161  sxs = sx->sxs + sx->nsxs;
162  sxs->len = stem_len;
163 #ifdef HAVE_STRNDUP
164 /*@i@*/ sxs->stem = strndup(*bpp, stem_len);
165 #else
166  sxs->stem = xmalloc(stem_len+1);
167  strncpy(sxs->stem, *bpp, stem_len);
168 #endif
169  sx->nsxs++;
170  *bpp += stem_len;
171  return sx->nsxs - 1;
172 }
173 
182 static int rpmsxFind(/*@null@*/ const rpmsx sx, const char ** bpp)
183  /*@modifies *bpp @*/
184 {
185  size_t stem_len = rpmsxsFStem(*bpp);
186  rpmsxs sxs;
187  int i;
188 
189  if (sx != NULL && stem_len > 0)
190  for (i = 0; i < sx->nsxs; i++) {
191  sxs = sx->sxs + i;
192  if (stem_len != sxs->len)
193  continue;
194 /*@i@*/ if (strncmp(*bpp, sxs->stem, stem_len))
195  continue;
196  *bpp += stem_len;
197  return i;
198  }
199  return -1;
200 }
201 
202 rpmsx XrpmsxUnlink(rpmsx sx, const char * msg, const char * fn, unsigned ln)
203 {
204  if (sx == NULL) return NULL;
205 /*@-modfilesys@*/
206 if (_rpmsx_debug && msg != NULL)
207 fprintf(stderr, "--> sx %p -- %d %s at %s:%u\n", sx, sx->nrefs, msg, fn, ln);
208 /*@=modfilesys@*/
209  sx->nrefs--;
210  return NULL;
211 }
212 
213 rpmsx XrpmsxLink(rpmsx sx, const char * msg, const char * fn, unsigned ln)
214 {
215  if (sx == NULL) return NULL;
216  sx->nrefs++;
217 
218 /*@-modfilesys@*/
219 if (_rpmsx_debug && msg != NULL)
220 fprintf(stderr, "--> sx %p ++ %d %s at %s:%u\n", sx, sx->nrefs, msg, fn, ln);
221 /*@=modfilesys@*/
222 
223  /*@-refcounttrans@*/ return sx; /*@=refcounttrans@*/
224 }
225 
227 {
228  int i;
229 
230  if (sx == NULL)
231  return NULL;
232 
233  if (sx->nrefs > 1)
234  return rpmsxUnlink(sx, __func__);
235 
236 /*@-modfilesys@*/
237 if (_rpmsx_debug < 0)
238 fprintf(stderr, "*** sx %p\t%s[%d]\n", sx, __func__, sx->Count);
239 /*@=modfilesys@*/
240 
241  /*@-branchstate@*/
242  if (sx->Count > 0)
243  for (i = 0; i < sx->Count; i++) {
244  rpmsxp sxp = sx->sxp + i;
245  sxp->pattern = _free(sxp->pattern);
246  sxp->type = _free(sxp->type);
247  sxp->context = _free(sxp->context);
248 /*@i@*/ regfree(sxp->preg);
249 /*@i@*/ sxp->preg = _free(sxp->preg);
250  }
251  sx->sxp = _free(sx->sxp);
252 
253  if (sx->nsxs > 0)
254  for (i = 0; i < sx->nsxs; i++) {
255  rpmsxs sxs = sx->sxs + i;
256  sxs->stem = _free(sxs->stem);
257  }
258  sx->sxs = _free(sx->sxs);
259  /*@=branchstate@*/
260 
261  (void) rpmsxUnlink(sx, __func__);
262  /*@-refcounttrans -usereleased@*/
263 /*@-boundswrite@*/
264  memset(sx, 0, sizeof(*sx)); /* XXX trash and burn */
265 /*@=boundswrite@*/
266  sx = _free(sx);
267  /*@=refcounttrans =usereleased@*/
268  return NULL;
269 }
270 
280 static int rpmsxpCheckNoDupes(const rpmsx sx)
281  /*@*/
282 {
283  int i, j;
284  int rc = 0;
285 
286  for (i = 0; i < sx->Count; i++) {
287  rpmsxp sxpi = sx->sxp + i;
288  for (j = i + 1; j < sx->Count; j++) {
289  rpmsxp sxpj = sx->sxp + j;
290 
291  /* Check if same RE string */
292  if (strcmp(sxpj->pattern, sxpi->pattern))
293  /*@innercontinue@*/ continue;
294  if (sxpj->fmode && sxpi->fmode && sxpj->fmode != sxpi->fmode)
295  /*@innercontinue@*/ continue;
296 
297  /* Same RE string found */
298  if (strcmp(sxpj->context, sxpi->context)) {
299  /* If different contexts, give warning */
300 /*@-modfilesys@*/
301  fprintf(stderr,
302  "ERROR: Multiple different specifications for %s (%s and %s).\n",
303  sxpi->pattern, sxpj->context, sxpi->context);
304 /*@=modfilesys@*/
305  rc = -1;
306  } else {
307  /* If same contexts give warning */
308 /*@-modfilesys@*/
309  fprintf(stderr,
310  "WARNING: Multiple same specifications for %s.\n",
311  sxpi->pattern);
312 /*@=modfilesys@*/
313  }
314  }
315  }
316  return rc;
317 }
318 
319 int rpmsxParse(rpmsx sx, const char * fn)
320 {
321  FILE * fp;
322  char buf[BUFSIZ + 1];
323  char * bp;
324  char * regex;
325  char * type;
326  char * context;
327  char * anchored_regex;
328  int items;
329  int len;
330  int lineno;
331  int pass;
332  int regerr;
333  int nerr = 0;
334 
335 #define inc_err() nerr++
336 
337 /*@-branchstate@*/
338  if (fn == NULL)
339  fn = "%{?__file_context_path}";
340 /*@=branchstate@*/
341 
342  { const char * myfn = rpmGetPath(fn, NULL);
343 
344  if (myfn == NULL || *myfn == '\0'
345  || (fp = fopen(myfn, "r")) == NULL)
346  {
347  myfn = _free(myfn);
348  return -1;
349  }
350  myfn = _free(myfn);
351  }
352 
353  /*
354  * Perform two passes over the specification file.
355  * The first pass counts the number of specifications and
356  * performs simple validation of the input. At the end
357  * of the first pass, the spec array is allocated.
358  * The second pass performs detailed validation of the input
359  * and fills in the spec array.
360  */
361 /*@-branchstate@*/
362  for (pass = 0; pass < 2; pass++) {
363  rpmsxp sxp;
364 
365  lineno = 0;
366  sx->Count = 0;
367  sxp = sx->sxp;
368  while (fgets(buf, sizeof(buf)-1, fp)) {
369  buf[sizeof(buf)-1] = '\0';
370  lineno++;
371  len = strlen(buf);
372  if (buf[len - 1] != '\n') {
373  fprintf(stderr,
374  _("%s: no newline on line number %d (only read %s)\n"),
375  fn, lineno, buf);
376  inc_err();
377  /*@innercontinue@*/ continue;
378  }
379  buf[len - 1] = 0;
380  bp = buf;
381  while (isspace(*bp))
382  bp++;
383  /* Skip comment lines and empty lines. */
384  if (*bp == '#' || *bp == 0)
385  /*@innercontinue@*/ continue;
386 /*@-formatcode@*/
387  items = sscanf(buf, "%as %as %as", &regex, &type, &context);
388 /*@=formatcode@*/
389  if (items < 2) {
390  fprintf(stderr,
391  _("%s: line number %d is missing fields (only read %s)\n"),
392  fn, lineno, buf);
393  inc_err();
394  if (items == 1)
395  free(regex);
396  /*@innercontinue@*/ continue;
397  } else if (items == 2) {
398  /* The type field is optional. */
399  free(context);
400  context = type;
401  type = 0;
402  }
403 
404  /* On pass 2, compile and store the specification. */
405  if (pass == 1) {
406  const char * reg_buf = regex;
407  sxp->fstem = rpmsxAdd(sx, &reg_buf);
408  sxp->pattern = regex;
409 
410  /* Anchor the regular expression. */
411  len = strlen(reg_buf);
412  anchored_regex = xmalloc(len + 3);
413  sprintf(anchored_regex, "^%s$", reg_buf);
414 
415  /* Compile the regular expression. */
416 /*@i@*/ sxp->preg = xcalloc(1, sizeof(*sxp->preg));
417  regerr = regcomp(sxp->preg, anchored_regex,
418  REG_EXTENDED | REG_NOSUB);
419  if (regerr < 0) {
420  char errbuf[BUFSIZ + 1];
421  (void) regerror(regerr, sxp->preg, errbuf, sizeof(errbuf)-1);
422  errbuf[sizeof(errbuf)-1] = '\0';
423  fprintf(stderr,
424  _("%s: unable to compile regular expression %s on line number %d: %s\n"),
425  fn, regex, lineno,
426  errbuf);
427  inc_err();
428  }
429  free(anchored_regex);
430 
431  /* Convert the type string to a mode format */
432  sxp->type = type;
433  sxp->fmode = 0;
434  if (!type)
435  goto skip_type;
436  len = strlen(type);
437  if (type[0] != '-' || len != 2) {
438  fprintf(stderr,
439  _("%s: invalid type specifier %s on line number %d\n"),
440  fn, type, lineno);
441  inc_err();
442  goto skip_type;
443  }
444  switch (type[1]) {
445  case 'b': sxp->fmode = S_IFBLK; /*@switchbreak@*/ break;
446  case 'c': sxp->fmode = S_IFCHR; /*@switchbreak@*/ break;
447  case 'd': sxp->fmode = S_IFDIR; /*@switchbreak@*/ break;
448  case 'p': sxp->fmode = S_IFIFO; /*@switchbreak@*/ break;
449  case 'l': sxp->fmode = S_IFLNK; /*@switchbreak@*/ break;
450 /*@i@*/ case 's': sxp->fmode = S_IFSOCK; /*@switchbreak@*/ break;
451  case '-': sxp->fmode = S_IFREG; /*@switchbreak@*/ break;
452  default:
453  fprintf(stderr,
454  _("%s: invalid type specifier %s on line number %d\n"),
455  fn, type, lineno);
456  inc_err();
457  /*@switchbreak@*/ break;
458  }
459 
460  skip_type:
461 
462  sxp->context = context;
463 
464  if (strcmp(context, "<<none>>")) {
465  if (security_check_context(context) < 0 && errno != ENOENT) {
466  fprintf(stderr,
467  _("%s: invalid context %s on line number %d\n"),
468  fn, context, lineno);
469  inc_err();
470  }
471  }
472 
473  /* Determine if specification has
474  * any meta characters in the RE */
475  rpmsxpHasMetaChars(sxp);
476  sxp++;
477  }
478 
479  sx->Count++;
480  if (pass == 0) {
481 /*@-kepttrans@*/
482  free(regex);
483  if (type)
484  free(type);
485  free(context);
486 /*@=kepttrans@*/
487  }
488  }
489 
490  if (nerr) {
491  (void) fclose(fp);
492  return -1;
493  }
494 
495  if (pass == 0) {
496  if (sx->Count == 0) {
497  (void) fclose(fp);
498  return 0;
499  }
500  sx->sxp = xcalloc(sx->Count, sizeof(*sx->sxp));
501  rewind(fp);
502  }
503  }
504 /*@=branchstate@*/
505  (void) fclose(fp);
506 
507  /* Stable sort for policy specifications, patterns before paths. */
508  rpmsxSort(sx);
509 
510  /* Verify no exact duplicates */
511  if (rpmsxpCheckNoDupes(sx) != 0)
512  return -1;
513 
514  return 0;
515 
516 }
517 
518 rpmsx rpmsxNew(const char * fn)
519 {
520  rpmsx sx;
521 
522  sx = xcalloc(1, sizeof(*sx));
523  sx->sxp = NULL;
524  sx->Count = 0;
525  sx->i = -1;
526  sx->sxs = NULL;
527  sx->nsxs = 0;
528  sx->maxsxs = 0;
529  sx->reverse = 0;
530 
531  (void) rpmsxLink(sx, __func__);
532 
533  if (rpmsxParse(sx, fn) != 0)
534  return rpmsxFree(sx);
535 
536  return sx;
537 }
538 
539 int rpmsxCount(const rpmsx sx)
540 {
541  return (sx != NULL ? sx->Count : 0);
542 }
543 
544 int rpmsxIx(const rpmsx sx)
545 {
546  return (sx != NULL ? sx->i : -1);
547 }
548 
549 int rpmsxSetIx(rpmsx sx, int ix)
550 {
551  int i = -1;
552 
553  if (sx != NULL) {
554  i = sx->i;
555  sx->i = ix;
556  }
557  return i;
558 }
559 
560 const char * rpmsxPattern(const rpmsx sx)
561 {
562  const char * pattern = NULL;
563 
564  if (sx != NULL && sx->i >= 0 && sx->i < sx->Count)
565  pattern = (sx->sxp + sx->i)->pattern;
566  return pattern;
567 }
568 
569 const char * rpmsxType(const rpmsx sx)
570 {
571  const char * type = NULL;
572 
573  if (sx != NULL && sx->i >= 0 && sx->i < sx->Count)
574  type = (sx->sxp + sx->i)->type;
575  return type;
576 }
577 
578 const char * rpmsxContext(const rpmsx sx)
579 {
580  const char * context = NULL;
581 
582  if (sx != NULL && sx->i >= 0 && sx->i < sx->Count)
583  context = (sx->sxp + sx->i)->context;
584  return context;
585 }
586 
587 regex_t * rpmsxRE(const rpmsx sx)
588 {
589  regex_t * preg = NULL;
590 
591  if (sx != NULL && sx->i >= 0 && sx->i < sx->Count)
592  preg = (sx->sxp + sx->i)->preg;
593  return preg;
594 }
595 
596 mode_t rpmsxFMode(const rpmsx sx)
597 {
598  mode_t fmode = 0;
599 
600  if (sx != NULL && sx->i >= 0 && sx->i < sx->Count)
601  fmode = (sx->sxp + sx->i)->fmode;
602  return fmode;
603 }
604 
605 int rpmsxFStem(const rpmsx sx)
606 {
607  int fstem = -1;
608 
609  if (sx != NULL && sx->i >= 0 && sx->i < sx->Count)
610  fstem = (sx->sxp + sx->i)->fstem;
611  return fstem;
612 }
613 
614 int rpmsxNext(/*@null@*/ rpmsx sx)
615  /*@modifies sx @*/
616 {
617  int i = -1;
618 
619  if (sx != NULL) {
620  if (sx->reverse != 0) {
621  i = --sx->i;
622  if (sx->i < 0) {
623  sx->i = sx->Count;
624  i = -1;
625  }
626  } else {
627  i = ++sx->i;
628  if (sx->i >= sx->Count) {
629  sx->i = -1;
630  i = -1;
631  }
632  }
633 
634 /*@-modfilesys @*/
635 if (_rpmsx_debug < 0 && i != -1) {
636 rpmsxp sxp = sx->sxp + i;
637 fprintf(stderr, "*** sx %p\t%s[%d]\t%s\t%s\n", sx, __func__, i, sxp->pattern, sxp->context);
638 /*@=modfilesys @*/
639 }
640 
641  }
642 
643  return i;
644 }
645 
646 rpmsx rpmsxInit(/*@null@*/ rpmsx sx, int reverse)
647  /*@modifies sx @*/
648 {
649  if (sx != NULL) {
650  sx->reverse = reverse;
651  sx->i = (sx->reverse ? sx->Count : -1);
652  }
653  /*@-refcounttrans@*/
654  return sx;
655  /*@=refcounttrans@*/
656 }
657 
658 const char * rpmsxFContext(rpmsx sx, const char * fn, mode_t fmode)
659 {
660  const char * fcontext = NULL;
661  const char * myfn = fn;
662 /*@-mods@*/
663  int fstem = rpmsxFind(sx, &myfn);
664 /*@=mods@*/
665  int i;
666 
667  sx = rpmsxInit(sx, 1);
668  if (sx != NULL)
669  while ((i = rpmsxNext(sx)) >= 0) {
670  regex_t * preg;
671  mode_t sxfmode;
672  int sxfstem;
673  int ret;
674 
675  sxfstem = rpmsxFStem(sx);
676  if (sxfstem != -1 && sxfstem != fstem)
677  continue;
678 
679  sxfmode = rpmsxFMode(sx);
680  if (sxfmode && (fmode & S_IFMT) != sxfmode)
681  continue;
682 
683  preg = rpmsxRE(sx);
684  if (preg == NULL)
685  continue;
686 
687  ret = regexec(preg, (sxfstem == -1 ? fn : myfn), 0, NULL, 0);
688  switch (ret) {
689  case REG_NOMATCH:
690  continue;
691  /*@notreached@*/ /*@switchbreak@*/ break;
692  case 0:
693  fcontext = rpmsxContext(sx);
694  /*@switchbreak@*/ break;
695  default:
696  { static char errbuf[BUFSIZ + 1];
697  (void) regerror(ret, preg, errbuf, sizeof(errbuf)-1);
698 /*@-modfilesys -nullpass @*/
699  errbuf[sizeof(errbuf)-1] = '\0';
700  fprintf(stderr, "unable to match %s against %s: %s\n",
701  fn, rpmsxPattern(sx), errbuf);
702 /*@=modfilesys =nullpass @*/
703  } /*@switchbreak@*/ break;
704  }
705  break;
706  }
707 
708  return fcontext;
709 }