1 /*
2  * This file is part of DGD, https://github.com/dworkin/dgd
3  * Copyright (C) 1993-2010 Dworkin B.V.
4  * Copyright (C) 2010-2013,2015 DGD Authors (see the commit log for details)
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Affero General Public License as
8  * published by the Free Software Foundation, either version 3 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Affero General Public License for more details.
15  *
16  * You should have received a copy of the GNU Affero General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 # define INCLUDE_FILE_IO
21 # include "kfun.h"
22 # include "table.h"
23 
24 /*
25  * prototypes
26  */
27 # define FUNCDEF(name, func, proto, v) extern int func(); extern char proto[];
28 # include "builtin.c"
29 # include "std.c"
30 # include "file.c"
31 # include "math.c"
32 # include "extra.c"
33 # undef FUNCDEF
34 
35 /*
36  * kernel function table
37  */
38 static kfunc kforig[] = {
39 # define FUNCDEF(name, func, proto, v) { name, proto, func, (extfunc) NULL, v },
40 # include "builtin.c"
41 # include "std.c"
42 # include "file.c"
43 # include "math.c"
44 # include "extra.c"
45 # undef FUNCDEF
46 };
47 
48 kfunc kftab[KFTAB_SIZE];	/* kfun tab */
49 kfunc kfenc[KFCRYPT_SIZE];	/* encryption */
50 kfunc kfdec[KFCRYPT_SIZE];	/* decryption */
51 kfunc kfhsh[KFCRYPT_SIZE];	/* hashing */
52 kfindex kfind[KFTAB_SIZE];	/* n -> index */
53 static kfindex kfx[KFTAB_SIZE];	/* index -> n */
54 int nkfun, ne, nd, nh;		/* # kfuns */
55 
56 extern void kf_enc(frame *, int, value *);
57 extern void kf_enc_key(frame *, int, value *);
58 extern void kf_dec(frame *, int, value *);
59 extern void kf_dec_key(frame *, int, value *);
60 extern void kf_xcrypt(frame *, int, value *);
61 extern void kf_md5(frame *, int, value *);
62 extern void kf_sha1(frame *, int, value *);
63 
64 /*
65  * NAME:	kfun->clear()
66  * DESCRIPTION:	clear previously added kfuns from the table
67  */
kf_clear()68 void kf_clear()
69 {
70     static char proto[] = { T_VOID, 0 };
71     static extkfunc builtin[] = {
72 	{ "encrypt DES", proto, kf_enc },
73 	{ "encrypt DES key", proto, kf_enc_key },
74 	{ "decrypt DES", proto, kf_dec },
75 	{ "decrypt DES key", proto, kf_dec_key },
76 	{ "hash MD5", proto, kf_md5 },
77 	{ "hash SHA1", proto, kf_sha1 },
78 	{ "hash crypt", proto, kf_xcrypt }
79     };
80 
81     nkfun = sizeof(kforig) / sizeof(kfunc);
82     ne = nd = nh = 0;
83     kf_ext_kfun(builtin, 7);
84 }
85 
86 /*
87  * NAME:	kfun->callgate()
88  * DESCRIPTION:	extra kfun call gate
89  */
kf_callgate(frame * f,int nargs,kfunc * kf)90 static int kf_callgate(frame *f, int nargs, kfunc *kf)
91 {
92     value val;
93 
94     val = nil_value;
95     (kf->ext)(f, nargs, &val);
96     i_ref_value(&val);
97     i_pop(f, nargs);
98     *--f->sp = val;
99 
100     return 0;
101 }
102 
103 /*
104  * NAME:	prototype()
105  * DESCRIPTION:	construct proper prototype for new kfun
106  */
prototype(char * proto)107 static char *prototype(char *proto)
108 {
109     char *p, *q;
110     int nargs, vargs;
111     int tclass, type;
112     bool varargs;
113 
114     tclass = C_STATIC;
115     type = *proto++;
116     p = proto;
117     nargs = vargs = 0;
118     varargs = FALSE;
119 
120     /* pass 1: check prototype */
121     if (*p != T_VOID) {
122 	while (*p != '\0') {
123 	    if (*p == T_VARARGS) {
124 		/* varargs or ellipsis */
125 		if (p[1] == '\0') {
126 		    tclass |= C_ELLIPSIS;
127 		    if (!varargs) {
128 			--nargs;
129 			vargs++;
130 		    }
131 		    break;
132 		}
133 		varargs = TRUE;
134 	    } else {
135 		if (*p != T_MIXED) {
136 		    if (*p == T_LVALUE) {
137 			/* lvalue arguments: turn off typechecking */
138 			tclass &= ~C_TYPECHECKED;
139 		    } else {
140 			/* non-mixed arguments: typecheck this function */
141 			tclass |= C_TYPECHECKED;
142 		    }
143 		}
144 		if (varargs) {
145 		    vargs++;
146 		} else {
147 		    nargs++;
148 		}
149 	    }
150 	    p++;
151 	}
152     }
153 
154     /* allocate new prototype */
155     p = proto;
156     q = proto = ALLOC(char, 6 + nargs + vargs);
157     *q++ = tclass;
158     *q++ = nargs;
159     *q++ = vargs;
160     *q++ = 0;
161     *q++ = 6 + nargs + vargs;
162     *q++ = type;
163 
164     /* pass 2: fill in new prototype */
165     if (*p != T_VOID) {
166 	while (*p != '\0') {
167 	    if (*p != T_VARARGS) {
168 		*q++ = *p;
169 	    }
170 	    p++;
171 	}
172     }
173 
174     return proto;
175 }
176 
177 /*
178  * NAME:	kfun->ext_kfun()
179  * DESCRIPTION:	add new kfuns
180  */
kf_ext_kfun(extkfunc * kfadd,int n)181 void kf_ext_kfun(extkfunc *kfadd, int n)
182 {
183     register kfunc *kf;
184 
185     for (; n != 0; kfadd++, --n) {
186 	if (strncmp(kfadd->name, "encrypt ", 8) == 0) {
187 	    kf = &kfenc[ne++];
188 	    kf->name = kfadd->name + 8;
189 	} else if (strncmp(kfadd->name, "decrypt ", 8) == 0) {
190 	    kf = &kfdec[nd++];
191 	    kf->name = kfadd->name + 8;
192 	} else if (strncmp(kfadd->name, "hash ", 5) == 0) {
193 	    kf = &kfhsh[nh++];
194 	    kf->name = kfadd->name + 5;
195 	} else {
196 	    kf = &kftab[nkfun++];
197 	    kf->name = kfadd->name;
198 	}
199 	kf->proto = prototype(kfadd->proto);
200 	kf->func = (int (*)()) &kf_callgate;
201 	kf->ext = kfadd->func;
202 	kf->version = 0;
203     }
204 }
205 
206 /*
207  * NAME:	kfun->cmp()
208  * DESCRIPTION:	compare two kftable entries
209  */
kf_cmp(cvoid * cv1,cvoid * cv2)210 static int kf_cmp(cvoid *cv1, cvoid *cv2)
211 {
212     return strcmp(((kfunc *) cv1)->name, ((kfunc *) cv2)->name);
213 }
214 
215 /*
216  * NAME:	kfun->init()
217  * DESCRIPTION:	initialize the kfun table
218  */
kf_init()219 void kf_init()
220 {
221     int i, n;
222     kfindex *k1, *k2;
223 
224     memcpy(kftab, kforig, sizeof(kforig));
225     for (i = 0, k1 = kfind, k2 = kfx; i < KF_BUILTINS; i++) {
226 	*k1++ = i;
227 	*k2++ = i;
228     }
229     qsort((void *) (kftab + KF_BUILTINS), nkfun - KF_BUILTINS,
230 	  sizeof(kfunc), kf_cmp);
231     qsort(kfenc, ne, sizeof(kfunc), kf_cmp);
232     qsort(kfdec, nd, sizeof(kfunc), kf_cmp);
233     qsort(kfhsh, nh, sizeof(kfunc), kf_cmp);
234     for (n = 0; kftab[i].name[1] == '.'; n++) {
235 	*k2++ = '\0';
236 	i++;
237     }
238     for (k1 = kfind + 128; i < nkfun; i++) {
239 	*k1++ = i;
240 	*k2++ = i + 128 - KF_BUILTINS - n;
241     }
242 }
243 
244 /*
245  * NAME:	kfun->index()
246  * DESCRIPTION:	search for kfun in the kfun table, return raw index or -1
247  */
kf_index(kfunc * kf,unsigned int l,unsigned int h,char * name)248 static int kf_index(kfunc *kf, unsigned int l, unsigned int h, char *name)
249 {
250     register unsigned int m;
251     register int c;
252 
253     do {
254 	c = strcmp(name, kf[m = (l + h) >> 1].name);
255 	if (c == 0) {
256 	    return m;	/* found */
257 	} else if (c < 0) {
258 	    h = m;	/* search in lower half */
259 	} else {
260 	    l = m + 1;	/* search in upper half */
261 	}
262     } while (l < h);
263     /*
264      * not found
265      */
266     return -1;
267 }
268 
269 /*
270  * NAME:	kfun->func()
271  * DESCRIPTION:	search for kfun in the kfun table, return index or -1
272  */
kf_func(char * name)273 int kf_func(char *name)
274 {
275     int n;
276 
277     n = kf_index(kftab, KF_BUILTINS, nkfun, name);
278     if (n >= 0) {
279 	n = kfx[n];
280     }
281     return n;
282 }
283 
284 /*
285  * NAME:	kfun->encrypt()
286  * DESCRIPTION:	encrypt a string
287  */
kf_encrypt(frame * f,int nargs)288 int kf_encrypt(frame *f, int nargs)
289 {
290     value val;
291     int n;
292 
293     n = kf_index(kfenc, 0, ne, f->sp[nargs - 1].u.string->text);
294     if (n < 0) {
295 	error("Unknown cipher");
296     }
297     val = nil_value;
298     (kfenc[n].ext)(f, nargs - 1, &val);
299     i_ref_value(&val);
300     i_pop(f, nargs);
301     *--f->sp = val;
302     return 0;
303 }
304 
305 /*
306  * NAME:	kfun->decrypt()
307  * DESCRIPTION:	decrypt a string
308  */
kf_decrypt(frame * f,int nargs)309 int kf_decrypt(frame *f, int nargs)
310 {
311     value val;
312     int n;
313 
314     n = kf_index(kfdec, 0, nd, f->sp[nargs - 1].u.string->text);
315     if (n < 0) {
316 	error("Unknown cipher");
317     }
318     val = nil_value;
319     (kfdec[n].ext)(f, nargs - 1, &val);
320     i_ref_value(&val);
321     i_pop(f, nargs);
322     *--f->sp = val;
323     return 0;
324 }
325 
326 /*
327  * NAME:	kfun->hash_string()
328  * DESCRIPTION:	hash a string
329  */
kf_hash_string(frame * f,int nargs)330 int kf_hash_string(frame *f, int nargs)
331 {
332     value val;
333     int n;
334 
335     n = kf_index(kfhsh, 0, nh, f->sp[nargs - 1].u.string->text);
336     if (n < 0) {
337 	error("Unknown hash algorithm");
338     }
339     val = nil_value;
340     (kfhsh[n].ext)(f, nargs - 1, &val);
341     i_ref_value(&val);
342     i_pop(f, nargs);
343     *--f->sp = val;
344     return 0;
345 }
346 
347 /*
348  * NAME:	kfun->reclaim()
349  * DESCRIPTION:	reclaim kfun space
350  */
kf_reclaim()351 void kf_reclaim()
352 {
353     int i, n, last;
354 
355     /* skip already-removed kfuns */
356     for (last = nkfun; kfind[--last + 128 - KF_BUILTINS] == '\0'; ) ;
357 
358     /* remove duplicates at the end */
359     for (i = last; i >= KF_BUILTINS; --i) {
360 	n = kfind[i + 128 - KF_BUILTINS];
361 	if (kfx[n] == i + 128 - KF_BUILTINS) {
362 	    if (i != last) {
363 		message("*** Reclaimed %d kernel function%s\012", last - i,
364 			((last - i > 1) ? "s" : ""));
365 	    }
366 	    break;
367 	}
368 	kfind[i + 128 - KF_BUILTINS] = '\0';
369     }
370 
371     /* copy last to 0.removed_kfuns */
372     for (i = KF_BUILTINS; i < nkfun && kftab[i].name[1] == '.'; i++) {
373 	if (kfx[i] != '\0') {
374 	    message("*** Preparing to reclaim unused kfun %s\012",
375 		    kftab[i].name);
376 	    n = kfind[last-- + 128 - KF_BUILTINS];
377 	    kfx[n] = kfx[i];
378 	    kfind[kfx[n]] = n;
379 	    kfx[i] = '\0';
380 	}
381     }
382 }
383 
384 
385 typedef struct {
386     short nbuiltin;	/* # builtin kfuns */
387     short nkfun;	/* # other kfuns */
388     short kfnamelen;	/* length of all kfun names */
389 } dump_header;
390 
391 static char dh_layout[] = "sss";
392 
393 /*
394  * NAME:	kfun->dump()
395  * DESCRIPTION:	dump the kfun table
396  */
kf_dump(int fd)397 bool kf_dump(int fd)
398 {
399     int i, n;
400     unsigned int len, buflen;
401     kfunc *kf;
402     dump_header dh;
403     char *buffer;
404     bool flag;
405 
406     /* prepare header */
407     dh.nbuiltin = KF_BUILTINS;
408     dh.nkfun = nkfun - KF_BUILTINS;
409     dh.kfnamelen = 0;
410     for (i = KF_BUILTINS; i < nkfun; i++) {
411 	n = kfind[i + 128 - KF_BUILTINS];
412 	if (kfx[n] != '\0') {
413 	    dh.kfnamelen += strlen(kftab[n].name) + 1;
414 	    if (kftab[n].name[1] != '.') {
415 		dh.kfnamelen += 2;
416 	    }
417 	} else {
418 	    --dh.nkfun;
419 	}
420     }
421 
422     /* write header */
423     if (P_write(fd, (char *) &dh, sizeof(dump_header)) < 0) {
424 	return FALSE;
425     }
426 
427     /* write kfun names */
428     buffer = ALLOCA(char, dh.kfnamelen);
429     buflen = 0;
430     for (i = KF_BUILTINS; i < nkfun; i++) {
431 	n = kfind[i + 128 - KF_BUILTINS];
432 	if (kfx[n] != '\0') {
433 	    kf = &kftab[n];
434 	    if (kf->name[1] != '.') {
435 		buffer[buflen++] = '0' + kf->version;
436 		buffer[buflen++] = '.';
437 	    }
438 	    len = strlen(kf->name) + 1;
439 	    memcpy(buffer + buflen, kf->name, len);
440 	    buflen += len;
441 	}
442     }
443     flag = (P_write(fd, buffer, buflen) >= 0);
444     AFREE(buffer);
445 
446     return flag;
447 }
448 
449 /*
450  * NAME:	kfun->restore()
451  * DESCRIPTION:	restore the kfun table
452  */
kf_restore(int fd,int oldcomp)453 void kf_restore(int fd, int oldcomp)
454 {
455     int i, n, buflen;
456     dump_header dh;
457     char *buffer;
458 
459     /* read header */
460     conf_dread(fd, (char *) &dh, dh_layout, (Uint) 1);
461 
462     /* fix kfuns */
463     buffer = ALLOCA(char, dh.kfnamelen);
464     if (P_read(fd, buffer, (unsigned int) dh.kfnamelen) < 0) {
465 	fatal("cannot restore kfun names");
466     }
467     memset(kfx + KF_BUILTINS, '\0', (nkfun - KF_BUILTINS) * sizeof(kfindex));
468     buflen = 0;
469     for (i = 0; i < dh.nkfun; i++) {
470 	if (buffer[buflen + 1] == '.') {
471 	    n = kf_index(kftab, KF_BUILTINS, nkfun, buffer + buflen + 2);
472 	    if (n < 0 || kftab[n].version != buffer[buflen] - '0') {
473 		n = kf_index(kftab, KF_BUILTINS, nkfun, buffer + buflen);
474 		if (n < 0) {
475 		    error("Restored unknown kfun: %s", buffer + buflen);
476 		}
477 	    }
478 	} else {
479 	    n = kf_index(kftab, KF_BUILTINS, nkfun, buffer + buflen);
480 	    if (n < 0) {
481 		if (strcmp(buffer + buflen, "(compile_object)") == 0) {
482 		    n = kf_index(kftab, KF_BUILTINS, nkfun, "0.compile_object");
483 		} else if (strcmp(buffer + buflen, "hash_md5") == 0 ||
484 			   strcmp(buffer + buflen, "(hash_md5)") == 0) {
485 		    n = kf_index(kftab, KF_BUILTINS, nkfun, "0.hash_md5");
486 		} else if (strcmp(buffer + buflen, "hash_sha1") == 0 ||
487 			   strcmp(buffer + buflen, "(hash_sha1)") == 0) {
488 		    n = kf_index(kftab, KF_BUILTINS, nkfun, "0.hash_sha1");
489 		} else {
490 		    error("Restored unknown kfun: %s", buffer + buflen);
491 		}
492 	    }
493 	    if (kftab[n].func == kf_dump_state) {
494 		n = kf_index(kftab, KF_BUILTINS, nkfun, "0.dump_state");
495 	    } else if (kftab[n].func == kf_old_compile_object) {
496 		oldcomp = FALSE;
497 	    } else if (kftab[n].func == kf_compile_object && oldcomp) {
498 		/* convert compile_object() */
499 		n = kf_index(kftab, KF_BUILTINS, nkfun, "0.compile_object");
500 	    }
501 	}
502 	kfx[n] = i + 128;
503 	kfind[i + 128] = n;
504 	buflen += strlen(buffer + buflen) + 1;
505     }
506     AFREE(buffer);
507 
508     if (dh.nkfun < nkfun - KF_BUILTINS) {
509 	/*
510 	 * There are more kfuns in the current driver than in the driver
511 	 * which created the snapshot: deal with those new kfuns.
512 	 */
513 	n = dh.nkfun + 128;
514 	for (i = KF_BUILTINS; i < nkfun; i++) {
515 	    if (kfx[i] == '\0' && kftab[i].name[1] != '.') {
516 		/* new kfun */
517 		kfind[n] = i;
518 		kfx[i] = n++;
519 	    }
520 	}
521     }
522 }
523