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