1 /*
2  * Copyright (c) 2005 Michael Schroeder (mls@suse.de)
3  *
4  * This program is licensed under the BSD license, read LICENSE.BSD
5  * for further information
6  */
7 
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <stdint.h>
11 #include <unistd.h>
12 #include <string.h>
13 #include <fcntl.h>
14 
15 #include <bzlib.h>
16 #include <zlib.h>
17 #include <lzma.h>
18 #include <sys/stat.h>
19 
20 #include "util.h"
21 #include "md5.h"
22 #include "rpmhead.h"
23 #include "cfile.h"
24 #include "deltarpm.h"
25 
26 /*****************************************************************
27  * fileblock handling, maintain everything we want to know about the
28  * filelist
29  *
30  */
31 
32 int
headtofb(struct rpmhead * h,struct fileblock * fb)33 headtofb(struct rpmhead *h, struct fileblock *fb)
34 {
35   unsigned int *digestalgoarray;
36   fb->h = h;
37   fb->filelinktos = fb->filemd5s = 0;
38   fb->filemodes = fb->filesizes = 0;
39   fb->filenames = headexpandfilelist(h, &fb->cnt);
40   if (!fb->filenames)
41     {
42       fb->cnt = 0;
43       return 0;
44     }
45   fb->filemodes = headint16(h, TAG_FILEMODES, (int *)0);
46   fb->filesizes = headint32(h, TAG_FILESIZES, (int *)0);
47   fb->filerdevs = headint16(h, TAG_FILERDEVS, (int *)0);
48   fb->filelinktos = headstringarray(h, TAG_FILELINKTOS, (int *)0);
49   fb->filemd5s = headstringarray(h, TAG_FILEMD5S, (int *)0);
50   fb->digestalgo = 1;
51   if ((digestalgoarray = headint32(h, TAG_FILEDIGESTALGO, (int *)0)))
52     {
53       fb->digestalgo = digestalgoarray[0];
54       free(digestalgoarray);
55     }
56   if (fb->digestalgo != 1 && fb->digestalgo != 8)
57     {
58       fprintf(stderr, "Unknown digest type: %d\n", fb->digestalgo);
59       exit(1);
60     }
61   return 0;
62 }
63 
64 /*****************************************************************
65  * sequence handling, uncompress the sequence string, check if
66  * it matches the installed rpm header, check files if requested.
67  */
68 
69 struct seqdescr *
expandseq(unsigned char * seq,int seql,int * nump,struct fileblock * fb,int (* checkfunc)(char *,int,unsigned char *,unsigned int))70 expandseq(unsigned char *seq, int seql, int *nump, struct fileblock *fb, int (*checkfunc)(char *, int, unsigned char *, unsigned int))
71 {
72   unsigned char *s;
73   char *fn;
74   int *res;
75   int i, n, n2, num, nib, shi, tog, jump, pos;
76   unsigned int rdev, lsize;
77   MD5_CTX seqmd5;
78   unsigned char seqmd5res[16];
79   struct seqdescr *sd;
80   drpmuint off;
81   unsigned char fmd5[32];
82   int error = 0;
83 
84   n = num = nib = shi = jump = pos = 0;
85   tog = 1;
86 
87   res = xmalloc2(fb->cnt, sizeof(unsigned int));
88   seql -= 16;
89   for (i = 0, s = seq + 16; i < seql; )
90     {
91       if (!nib)
92         n2 = (*s >> 4);
93       else
94 	{
95           n2 = (*s & 0x0f);
96 	  s++;
97 	  i++;
98 	}
99       nib ^= 1;
100       if (n2 & 8)
101 	{
102 	  n2 ^= 8;
103 	  if (shi)
104 	    n2 <<= shi;
105 	  n |= n2;
106 	  shi += 3;
107 	  continue;
108 	}
109       if (shi)
110 	n2 <<= shi;
111       shi = 0;
112       n2 |= n;
113       n = 0;
114       if (jump)
115 	{
116 	  jump = 0;
117 	  pos = n2;
118 	  tog = 1;
119           continue;
120 	}
121       if (n2 == 0)
122 	{
123 	  jump = 1;
124 	  continue;
125 	}
126       if (!tog)
127 	{
128 	  pos += n2;
129 	  tog = 1;
130 	  continue;
131 	}
132       for (; n2 > 0; n2--)
133 	{
134 	  if (num >= fb->cnt || pos >= fb->cnt)
135 	    {
136 	      fprintf(stderr, "corrupt delta: bad sequence\n");
137 	      exit(1);
138 	    }
139 	  res[num++] = pos++;
140 	}
141       tog = 0;
142     }
143   if (shi)
144     {
145       fprintf(stderr, "corrupt delta: bad sequence\n");
146       exit(1);
147     }
148   res = xrealloc2(res, num, sizeof(unsigned int));
149   sd = xmalloc2(num + 1, sizeof(*sd));
150   if (nump)
151     *nump = num + 1;
152   rpmMD5Init(&seqmd5);
153   off = 0;
154   for (n = 0; n < num; n++)
155     {
156       i = sd[n].i = res[n];
157       lsize = rdev = 0;
158       if (S_ISREG(fb->filemodes[i]))
159 	lsize = fb->filesizes[i];
160       else if (S_ISLNK(fb->filemodes[i]))
161 	lsize = strlen(fb->filelinktos[i]);
162       if (S_ISBLK(fb->filemodes[i]) || S_ISCHR(fb->filemodes[i]))
163 	rdev = fb->filerdevs[i];
164       fn = fb->filenames[i];
165       if (*fn == '/')
166 	fn++;
167       rpmMD5Update(&seqmd5, (unsigned char *)fn, strlen(fn) + 1);
168       rpmMD5Update32(&seqmd5, fb->filemodes[i]);
169       rpmMD5Update32(&seqmd5, lsize);
170       rpmMD5Update32(&seqmd5, rdev);
171       sd[n].cpiolen = 110 + 2 + strlen(fn) + 1;
172       if (sd[n].cpiolen & 3)
173 	sd[n].cpiolen += 4 - (sd[n].cpiolen & 3);
174       sd[n].datalen = lsize;
175       if (sd[n].datalen & 3)
176 	sd[n].datalen += 4 - (sd[n].datalen & 3);
177       if (S_ISLNK(fb->filemodes[i]))
178 	rpmMD5Update(&seqmd5, (unsigned char *)fb->filelinktos[i], strlen(fb->filelinktos[i]) + 1);
179       else if (S_ISREG(fb->filemodes[i]) && lsize)
180 	{
181 	  if (fb->digestalgo == 1)
182 	    parsemd5(fb->filemd5s[i], fmd5);
183 	  else
184 	    parsesha256(fb->filemd5s[i], fmd5);
185 	  if (checkfunc && checkfunc(fb->filenames[i], fb->digestalgo, fmd5, lsize))
186 	    error = 1;
187 	  if (fb->digestalgo == 1)
188 	    rpmMD5Update(&seqmd5, fmd5, 16);
189 	  else
190 	    rpmMD5Update(&seqmd5, fmd5, 32);
191 	}
192       sd[n].off = off;
193       off += sd[n].cpiolen + sd[n].datalen;
194       sd[n].f = 0;
195     }
196   sd[n].i = -1;
197   sd[n].cpiolen = 124;
198   sd[n].datalen = 0;
199   sd[n].off = off;
200   sd[n].f = 0;
201   rpmMD5Final(seqmd5res, &seqmd5);
202   free(res);
203   if (memcmp(seqmd5res, seq, 16) || error)
204     {
205       fprintf(stderr, "delta does not match installed data\n");
206       exit(1);
207     }
208   return sd;
209 }
210 
bzread4(struct cfile * bfp)211 static unsigned int bzread4(struct cfile *bfp)
212 {
213   unsigned char d[4];
214   if (bfp->read(bfp, d, 4) != 4)
215     {
216       perror("bzread4 error");
217       exit(1);
218     }
219   return d[0] << 24 | d[1] << 16 | d[2] << 8 | d[3];
220 }
221 
222 void
readdeltarpm(char * n,struct deltarpm * d,struct cfile ** cfp)223 readdeltarpm(char *n, struct deltarpm *d, struct cfile **cfp)
224 {
225   int dfd;
226   struct cfile *bfp;
227   unsigned int nevrl;
228   drpmuint off;
229   int i;
230 
231   memset((char *)d, 0, sizeof(*d));
232   d->name = n;
233   if (!strcmp(n, "-"))
234     dfd = 0;
235   else if ((dfd = open(n, O_RDONLY)) < 0)
236     {
237       perror(n);
238       exit(1);
239     }
240   if (xread(dfd, d->rpmlead, 12) != 12)
241     {
242       fprintf(stderr, "%s: not a delta rpm\n", n);
243       exit(1);
244     }
245   if (d->rpmlead[0] == 'd' && d->rpmlead[1] == 'r' && d->rpmlead[2] == 'p' && d->rpmlead[3] == 'm')
246     {
247       unsigned char *p;
248       d->version = (d->rpmlead[4] << 24) | (d->rpmlead[5] << 16) | (d->rpmlead[6] << 8) | d->rpmlead[7];
249       if ((d->version & 0xffffff00) != 0x444c5400)
250 	{
251 	  fprintf(stderr, "%s: not a delta rpm\n", n);
252 	  exit(1);
253 	}
254       if (d->version != 0x444c5431 && d->version != 0x444c5432 && d->version != 0x444c5433)
255 	{
256 	  fprintf(stderr, "%s: unsupported version: %c\n", n, (d->version & 255));
257 	  exit(1);
258 	}
259       nevrl = (d->rpmlead[8] << 24) | (d->rpmlead[9] << 16) | (d->rpmlead[10] << 8) | d->rpmlead[11];
260       d->targetnevr = xmalloc(nevrl + 4);	/* also room for 4 bytes addblklen */
261       if (xread(dfd, d->targetnevr, nevrl + 4) != nevrl + 4)
262 	{
263 	  fprintf(stderr, "%s: read error add data\n", n);
264 	  exit(1);
265 	}
266       p = (unsigned char *)d->targetnevr + nevrl;
267       d->addblklen = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
268       d->targetnevr[nevrl] = 0;
269       if (d->addblklen)
270 	{
271 	  d->addblk = xmalloc(d->addblklen);
272 	  if (xread(dfd, d->addblk, d->addblklen) != d->addblklen)
273 	    {
274 	      fprintf(stderr, "%s: read error add data\n", n);
275 	      exit(1);
276 	    }
277 	}
278       d->h = 0;
279       if ((bfp = cfile_open(CFILE_OPEN_RD, dfd, 0, CFILE_COMP_XX, CFILE_LEN_UNLIMITED, 0, 0)) == 0)
280 	{
281 	  fprintf(stderr, "%s: payload open failed\n", n);
282 	  exit(1);
283 	}
284     }
285   else
286     {
287       if (d->rpmlead[0] != 0xed || d->rpmlead[1] != 0xab || d->rpmlead[2] != 0xee || d->rpmlead[3] != 0xdb)
288 	{
289 	  fprintf(stderr, "%s: not a delta rpm\n", n);
290 	  exit(1);
291 	}
292       if (xread(dfd, d->rpmlead + 12, 96 - 12) != 96 - 12)
293 	{
294 	  fprintf(stderr, "%s: not a delta rpm\n", n);
295 	  exit(1);
296 	}
297       if (d->rpmlead[4] != 0x03 || d->rpmlead[0x4e] != 0 || d->rpmlead[0x4f] != 5)
298 	{
299 	  fprintf(stderr, "%s: not a v3 rpm or not new header styles\n", n);
300 	  exit(1);
301 	}
302       d->h = readhead(dfd, 1);
303       if (!d->h)
304 	{
305 	  fprintf(stderr, "%s: could not read signature header\n", n);
306 	  exit(1);
307 
308 	}
309       free(d->h);
310       d->h = readhead(dfd, 0);
311       if (!d->h)
312 	{
313 	  fprintf(stderr, "%s: could not read header\n", n);
314 	  exit(1);
315 	}
316       d->targetnevr = headtonevr(d->h);
317       if ((bfp = cfile_open(CFILE_OPEN_RD, dfd, 0, CFILE_COMP_XX, CFILE_LEN_UNLIMITED, 0, 0)) == 0)
318 	{
319 	  fprintf(stderr, "%s: payload open failed\n", n);
320 	  exit(1);
321 	}
322       d->addblklen = 0;
323     }
324   d->deltacomp = bfp->comp;
325   d->version = bzread4(bfp);
326   if ((d->version & 0xffffff00) != 0x444c5400)
327     {
328       fprintf(stderr, "%s: not a delta rpm\n", n);
329       exit(1);
330     }
331   if (d->version != 0x444c5431 && d->version != 0x444c5432 && d->version != 0x444c5433)
332     {
333       fprintf(stderr, "%s: unsupported version: %c\n", n, (d->version & 255));
334       exit(1);
335     }
336   if (!d->h && d->version < 0x444c5433)
337     {
338       fprintf(stderr, "%s: rpm only deltarpm with old version\n", n);
339       exit(1);
340     }
341   nevrl = bzread4(bfp);
342   d->nevr = xmalloc(nevrl + 1);
343   d->nevr[nevrl] = 0;
344   if (bfp->read(bfp, d->nevr, nevrl) != nevrl)
345     {
346       fprintf(stderr, "%s: read error nevr\n", n);
347       exit(1);
348     }
349   d->seql = bzread4(bfp);
350   if (d->seql < 16)
351     {
352       fprintf(stderr, "%s: corrupt delta\n", n);
353       exit(1);
354     }
355   d->seq = xmalloc(d->seql);
356   if (bfp->read(bfp, d->seq, d->seql) != d->seql)
357     {
358       fprintf(stderr, "%s: read error seq\n", n);
359       exit(1);
360     }
361   if (bfp->read(bfp, d->targetmd5, 16) != 16)
362     {
363       fprintf(stderr, "%s: read error md5\n", n);
364       exit(1);
365     }
366   d->targetcomppara = 0;
367   d->offadjn = 0;
368   d->offadjs = 0;
369   if (d->version != 0x444c5431)
370     {
371       d->targetsize = bzread4(bfp);
372       d->targetcomp = bzread4(bfp);
373       d->targetcompparalen = bzread4(bfp);
374       if (d->targetcompparalen)
375 	{
376 	  d->targetcomppara = xmalloc(d->targetcompparalen);
377 	  if (bfp->read(bfp, d->targetcomppara, d->targetcompparalen) != d->targetcompparalen)
378 	    {
379 	      fprintf(stderr, "%s: read error comppara\n", n);
380 	      exit(1);
381 	    }
382 	}
383       if (d->version != 0x444c5432)
384 	{
385 	  d->compheadlen = bzread4(bfp);
386 	  d->offadjn = bzread4(bfp);
387 	  d->offadjs = 0;
388 	  if (d->offadjn)
389 	    {
390 	      d->offadjs = xmalloc2(d->offadjn, 2 * sizeof(unsigned int));
391 	      for (i = 0; i < d->offadjn; i++)
392 		d->offadjs[2 * i] = bzread4(bfp);
393 	      for (i = 0; i < d->offadjn; i++)
394 		{
395 		  unsigned int a = bzread4(bfp);
396 		  if ((a & 0x80000000) != 0)
397 		    a = (unsigned int)(-(int)(a ^ 0x80000000));
398 		  d->offadjs[2 * i + 1] = a;
399 		}
400 	    }
401 	}
402     }
403   else
404     {
405       char *compressor = headstring(d->h, TAG_PAYLOADCOMPRESSOR);
406       if (compressor && !strcmp(compressor, "lzma"))
407 	d->targetcomp = CFILE_COMP_LZMA;
408       else if (compressor && !strcmp(compressor, "bzip2"))
409 	d->targetcomp = CFILE_COMP_BZ;
410       else
411 	d->targetcomp = CFILE_COMP_GZ;
412       d->targetsize = 0;
413       d->targetcompparalen = 0;
414     }
415   d->leadl = bzread4(bfp);
416   if (d->leadl < 96 + 16)
417     {
418       fprintf(stderr, "%s: corrupt delta\n", n);
419       exit(1);
420     }
421   d->lead = xmalloc(d->leadl);
422   if (bfp->read(bfp, d->lead, d->leadl) != d->leadl)
423     {
424       fprintf(stderr, "%s: read error lead\n", n);
425       exit(1);
426     }
427   d->payformatoff = bzread4(bfp);
428   if (d->h && d->payformatoff > d->h->dcnt - 4)
429     {
430       fprintf(stderr, "%s: bad payformat offset\n", n);
431       exit(1);
432     }
433   d->inn = bzread4(bfp);
434   d->outn = bzread4(bfp);
435   d->in = xmalloc2(d->inn, 2 * sizeof(unsigned int));
436   d->out = xmalloc2(d->outn, 2 * sizeof(unsigned int));
437   d->paylen = 0;
438   for (i = 0; i < d->inn; i++)
439     d->in[2 * i] = bzread4(bfp);
440   for (i = 0; i < d->inn; i++)
441     {
442       d->in[2 * i + 1] = bzread4(bfp);
443       d->paylen += d->in[2 * i + 1];
444     }
445   for (i = 0; i < d->outn; i++)
446     d->out[2 * i] = bzread4(bfp);
447   for (i = 0; i < d->outn; i++)
448     {
449       d->out[2 * i + 1] = bzread4(bfp);
450       d->paylen += d->out[2 * i + 1];
451     }
452 
453   d->outlen = 0;
454   if (d->version > 0x444c5432)
455     {
456 #ifdef DELTARPM_64BIT
457       d->outlen = (drpmuint)bzread4(bfp) << 32;
458 #else
459       if (bzread4(bfp) != 0)
460 	{
461 	  fprintf(stderr, "%s: deltarpm needs support for archives > 4GB\n", n);
462 	  exit(1);
463 	}
464 #endif
465     }
466   d->outlen |= bzread4(bfp);
467   if (d->addblklen)
468     {
469       if (bzread4(bfp))
470 	{
471 	  fprintf(stderr, "%s: two add data blocks\n", n);
472 	  exit(1);
473 	}
474     }
475   else
476     {
477       d->addblklen = bzread4(bfp);
478       if (d->addblklen)
479 	{
480 	  d->addblk = xmalloc(d->addblklen);
481 	  if (bfp->read(bfp, d->addblk, d->addblklen) != d->addblklen)
482 	    {
483 	      fprintf(stderr, "%s: read error add data\n", n);
484 	      exit(1);
485 	    }
486 	}
487     }
488   d->inlen = 0;
489   if (d->version > 0x444c5432)
490     {
491 #ifdef DELTARPM_64BIT
492       d->inlen = (drpmuint)bzread4(bfp) << 32;
493 #else
494       if (bzread4(bfp) != 0)
495 	{
496 	  fprintf(stderr, "%s: deltarpm needs support for archives > 4GB\n", n);
497 	  exit(1);
498 	}
499 #endif
500     }
501   d->inlen |= bzread4(bfp);
502   if (cfp)
503     *cfp = bfp;
504   else
505     {
506       d->indata = xmalloc(d->inlen);
507       if (bfp->read(bfp, d->indata, d->inlen) != d->inlen)
508 	{
509 	  fprintf(stderr, "%s: read error deltarpm data\n", n);
510 	  exit(1);
511 	}
512       bfp->close(bfp);
513     }
514   off = 0;
515   for (i = 0; i < d->inn; i++)
516     {
517       off += d->in[2 * i + 1];
518       if (off > d->inlen)
519 	{
520 	  fprintf(stderr, "%s: corrupt delta instructions\n", n);
521 	  exit(1);
522 	}
523     }
524   off = 0;
525   for (i = 0; i < d->outn; i++)
526     {
527       if (d->out[2 * i] & 0x80000000)
528 	d->out[2 * i] = (unsigned int)(-(int)(d->out[2 * i] ^ 0x80000000));
529       off += (int)d->out[2 * i];
530       if (off > d->outlen)
531 	{
532 	  fprintf(stderr, "corrupt delta instructions (outdata off %llu > %llu)\n", (unsigned long long)off, (unsigned long long)d->outlen);
533 	  exit(1);
534 	}
535       off += d->out[2 * i + 1];
536       if (off < 1 || off > d->outlen)
537 	{
538 	  fprintf(stderr, "corrupt delta instructions (outdata off + len %llu > %llu)\n", (unsigned long long)off, (unsigned long long)d->outlen);
539 	  exit(1);
540 	}
541     }
542   d->sdesc = 0;
543   d->nsdesc = 0;
544   d->outptr = 0;
545   d->next = d->prev = 0;
546   if (!cfp && strcmp(d->name, "-") != 0)
547     close(dfd);
548 }
549