V10/cmd/sign/verify.c

Compare this file to the similar file:
Show the results in this format:

#include "regexp.h"
#include <stdio.h>
#include <string.h>

/* version history of certificates
version 0, installed  Jun 6 1991
	Signed by name, ctime-date
	sum=string, count=number
	------
	message
	------
	sum=string, count=number
	End name, ctime-date
version 1, installed  Jun 10 1991
	Signed by name, ctime-date
	sum=string, date=gmtime-hex, count=number, ver=1
	------
	message with comma before each line, counted in sum
	------
	sum=string, date=gmtime-hex, count=number, ver=1
	End name, ctime-date
*/

extern char *malloc(), *realloc();
extern long time();
extern char *ctime();
extern void exit();
extern char *getline();

#define SIZEOF(a) (sizeof(a)/sizeof(*a))

int version = 0;

struct field {
	char *name, *pat;
	int len, seq;
	char *val;
};

struct field head[] = {
	{ "signer", "^..?.?.?.?.?.?.?$" },
	{ "ctime", "^... ... .. ..:..:.. ....$" }
};
enum { NAME, CTIME };

struct field data[] = { 
	{ "sum", "^[0-9a-f]+$" },
	{ "date", "^[0-9a-f]+$" },
	{ "count", "^[0-9]+$" },
	{ "ver", "^[0-9]+$" }
};
enum { SUM, DATE, COUNT, VERS };
int ndata[] = { 2, 4 };	/* # of fields in version */

char occ[][SIZEOF(data)] = {	/* sequence of fields in version*/
	{ 1,0,2,0 },
	{ 1,2,3,4 }
};

main(ac,av)
char **av;
{
	regsubexp mv[10];
	char *s, *body;
	int headlen, len1;
	long tloc;
	char *now;
	int i, lines;
	char canon[50];
	FILE *fil = stdin;
	int sflag = 0;
	regexp *l1= regcomp("Signed by (.+), (.+)$");
	char *pat = "signer=\\1, ctime=\\2";
	regexp *dashes = regcomp("------$");
	regexp *l3 = regcomp("End (.+), (.+)$");

	if(ac>1 && strcmp(av[1],"-s")==0) {
		sflag++;
		av++;
		ac--;
	}
	if(ac>1 && (fil=fopen(av[1],"r"))==0) {
		write(2,"verify: ",8);
		perror(av[1]);
		exit(1);
	}
	
	while(s=getline(fil)) {
		if(regexec(l1,s,mv,3)==0) continue;
		regsub(pat,canon,mv,3);	/* make the non-u uniform */
		if(parse(canon,head,SIZEOF(head))==2)
			break;
		for(i=0; i<SIZEOF(head); i++)
			head[i].val = 0;
	}
	if(s==0) error("cannot find signature line");

	s = getline(fil);
	if(s==0 || parse(s,data,SIZEOF(data))<2)
		error("cannot identify checksum line");
	version = data[VERS].val==0? 0: atoi(data[VERS].val);
	if(version >= SIZEOF(ndata))
		error("improper version number");
	for(i=0; i<SIZEOF(data); i++)
		if(data[i].seq != occ[version][i])
			error("incorrect field sequence");
	
	s = getline(fil);
	if(s==0 || regexec(dashes,s,mv,0)==0)
		error("cannot find beginning cut mark");

	len1 = atoi(data[COUNT].val);
	headlen = head[CTIME].len;
	if(version==1)
		headlen += data[DATE].len+head[NAME].len;
	body = malloc(len1+headlen+1);
	if(body==0) overflow();
	strcpy(body,head[CTIME].val);
	if(version!=0)
		strcat(strcat(body,data[DATE].val),head[NAME].val);
	if(input(body+headlen,len1,fil)==0)
		error("cannot read full signed text");

	s = getline(fil);
	if(s==0 || regexec(dashes,s,mv,0)==0)
		error("cannot find ending cut mark");

	s = getline(fil);
	if(s==0 || parse(s,data,SIZEOF(data))!=ndata[version])
		error("cannot find matching checksum line");

	s = getline(fil);
	if(s==0 || regexec(l3,s,mv,3)==0)
		error("cannot find matching signature line");
	regsub(pat,canon,mv,3);
	if(parse(canon,head,SIZEOF(head))!=2)
		error("cannot find matching signature line");

	lines = 0;
	if(version!=0) for(i=headlen+len1; --i>=headlen; )
		if(body[i]=='\n') lines++;

	switch(verify(head[NAME].val, data[SUM].val, body, headlen+len1)){
	case 1:
		(void)time(&tloc);
		now = ctime(&tloc);
		printf("Signature by %s, %s\n",
			head[NAME].val, head[DATE].val);
		printf("verified %.24s, count=%d\n",now, len1-lines);
		if(sflag) exit(0);
		printf("-----\n");
		output(body+headlen,len1);
		printf("-----\n");
		printf("verified %.24s, count=%d\n",now, len1-lines);
		printf("End %s, %s\n",
			head[NAME].val, head[DATE].val);
		exit(0);
	case 0:
		fprintf(stderr,"verify: Bogus\n");
		exit(1);
	case -1:
		fprintf(stderr,"verify: server unavailable; try later\n");
		exit(2);
	}
	/*NOTREACHED*/
}

error(err)
char *err;
{
	fprintf(stderr,"verify: document format error: %s\n", err);
	exit(1);
}

overflow()
{
	fprintf(stderr,"verify: out of space\n");
	exit(2);
}

output(s, n)
char *s;
{
	int d = version!=0;

	s += d;
	n -= d;
	while(--n >= 0) {
		putchar(*s);
		if(*s == '\n')
			s+=d, n-=d;
		s++;
	}
}

input(s, n, fil)
char *s;
FILE *fil;
{
	int c, nl;

	nl = 1;
	while(n>0) {
		c = getc(fil);
		if(c==EOF) break;
		if(nl && version!=0 && c!=',')
			continue;
		nl = 0;
		*s++ = c;
		n--;
		if(c=='\n') nl++;
	}
	return n==0;
}

parse(s,field,n)	/* extract name=val fields */
char *s;
struct field *field;
{
	regsubexp mv[3];
	int c = ',';
	regexp *nameval, *re;
	int seq, len, i;

	nameval = regcomp("^ *([a-z]+)=([^,]+)");
	if(nameval==0) overflow();

	for(seq=1; c==',' && regexec(nameval,s,mv,3); seq++) {
		c = mv[0].ep[0];
		s = mv[0].ep + 1;
		for(i=0; i<n; i++) {
			mv[1].ep[0] = 0;
			if(strcmp(field[i].name,mv[1].sp)==0)
				break;
		}
		if(i >= n)
			return -1;
		len = mv[2].ep - mv[2].sp;
		if(field[i].val==0) {	/* 1st time make entry */
			field[i].len = len;
			field[i].seq = seq;
			mv[2].ep[0] = 0;
			field[i].val = strdup(mv[2].sp);
			if(field[i].val==0) overflow();
			re = regcomp(field[i].pat);
			if(re==0) overflow();
			if(regexec(re,field[i].val,mv,0)==0)
				return -1;
			free((char*)re);
		}
		else		/* 2nd time check entry (hack) */
			if(field[i].len!=len || field[i].seq!=seq ||
			   strncmp(field[i].val,mv[2].sp,len)!=0)
				return -1;
	}
	free((char*)nameval);
	return c!=0? -1: seq-1;
}

char *
getline(fil)
FILE *fil;
{
	static char *s;
	static len;
	int n, c;

	if(s==0) {
		len = BUFSIZ;
		s = malloc(len);
		if(s==0) overflow();
	}
	n = 0;
	for(;;) {
		if(n>=len) {
			len += BUFSIZ;
			s = realloc(s,len);
			if(s==0) overflow();
		}
		c = getc(fil);
		switch(c) {
		case EOF:
			return 0;
		case '\n':
			s[n] = 0;
			return s;
		default:
			s[n++] = c;
		}
	}
}