]> git.lizzy.rs Git - plan9front.git/blob - sys/src/libauthsrv/readnvram.c
snoopy: dont rely on atoi() being able to parse hex
[plan9front.git] / sys / src / libauthsrv / readnvram.c
1 #include <u.h>
2 #include <libc.h>
3 #include <authsrv.h>
4
5 static long     finddosfile(int, char*);
6
7 static int
8 check(void *x, int len, uchar sum, char *msg)
9 {
10         if(nvcsum(x, len) == sum)
11                 return 0;
12         memset(x, 0, len);
13         fprint(2, "%s\n", msg);
14         return 1;
15 }
16
17 /*
18  *  get key info out of nvram.  since there isn't room in the PC's nvram use
19  *  a disk partition there.
20  */
21 static struct {
22         char *cputype;
23         char *file;
24         int off;
25         int len;
26 } nvtab[] = {
27         "sparc", "#r/nvram", 1024+850, sizeof(Nvrsafe),
28         "pc", "#S/sdC0/nvram", 0, sizeof(Nvrsafe),
29         "pc", "#S/sdC0/9fat", -1, sizeof(Nvrsafe),
30         "pc", "#S/sdC1/nvram", 0, sizeof(Nvrsafe),
31         "pc", "#S/sdC1/9fat", -1, sizeof(Nvrsafe),
32         "pc", "#S/sdD0/nvram", 0, sizeof(Nvrsafe),
33         "pc", "#S/sdD0/9fat", -1, sizeof(Nvrsafe),
34         "pc", "#S/sdE0/nvram", 0, sizeof(Nvrsafe),
35         "pc", "#S/sdE0/9fat", -1, sizeof(Nvrsafe),
36         "pc", "#S/sdF0/nvram", 0, sizeof(Nvrsafe),
37         "pc", "#S/sdF0/9fat", -1, sizeof(Nvrsafe),
38         "pc", "#S/sd00/nvram", 0, sizeof(Nvrsafe),
39         "pc", "#S/sd00/9fat", -1, sizeof(Nvrsafe),
40         "pc", "#S/sd01/nvram", 0, sizeof(Nvrsafe),
41         "pc", "#S/sd01/9fat", -1, sizeof(Nvrsafe),
42         "pc", "#S/sd10/nvram", 0, sizeof(Nvrsafe),
43         "pc", "#S/sd10/9fat", -1, sizeof(Nvrsafe),
44         "pc", "#f/fd0disk", -1, 512,    /* 512: #f requires whole sector reads */
45         "pc", "#f/fd1disk", -1, 512,
46         "mips", "#r/nvram", 1024+900, sizeof(Nvrsafe),
47         "power", "#F/flash/flash0", 0x440000, sizeof(Nvrsafe),
48         "power", "#F/flash/flash", 0x440000, sizeof(Nvrsafe),
49         "power", "#r/nvram", 4352, sizeof(Nvrsafe),     /* OK for MTX-604e */
50         "power", "/nvram", 0, sizeof(Nvrsafe),  /* OK for Ucu */
51         "arm", "#F/flash/flash0", 0x100000, sizeof(Nvrsafe),
52         "arm", "#F/flash/flash", 0x100000, sizeof(Nvrsafe),
53         "debug", "/tmp/nvram", 0, sizeof(Nvrsafe),
54 };
55
56 static char*
57 readcons(char *prompt, char *def, int raw, char *buf, int nbuf)
58 {
59         int fdin, fdout, ctl, n, m;
60         char line[10];
61
62         fdin = open("/dev/cons", OREAD);
63         if(fdin < 0)
64                 fdin = 0;
65         fdout = open("/dev/cons", OWRITE);
66         if(fdout < 0)
67                 fdout = 1;
68         if(def != nil)
69                 fprint(fdout, "%s[%s]: ", prompt, def);
70         else
71                 fprint(fdout, "%s: ", prompt);
72         if(raw){
73                 ctl = open("/dev/consctl", OWRITE);
74                 if(ctl >= 0)
75                         write(ctl, "rawon", 5);
76         } else
77                 ctl = -1;
78
79         m = 0;
80         for(;;){
81                 n = read(fdin, line, 1);
82                 if(n == 0){
83                         close(ctl);
84                         werrstr("readcons: EOF");
85                         return nil;
86                 }
87                 if(n < 0){
88                         close(ctl);
89                         werrstr("can't read cons");
90                         return nil;
91                 }
92                 if(line[0] == 0x7f)
93                         exits(0);
94                 if(n == 0 || line[0] == '\n' || line[0] == '\r'){
95                         if(raw){
96                                 write(ctl, "rawoff", 6);
97                                 write(fdout, "\n", 1);
98                                 close(ctl);
99                         }
100                         buf[m] = '\0';
101                         if(buf[0]=='\0' && def)
102                                 strcpy(buf, def);
103                         return buf;
104                 }
105                 if(line[0] == '\b'){
106                         if(m > 0)
107                                 m--;
108                 }else if(line[0] == 0x15){      /* ^U: line kill */
109                         m = 0;
110                         if(def != nil)
111                                 fprint(fdout, "%s[%s]: ", prompt, def);
112                         else
113                                 fprint(fdout, "%s: ", prompt);
114                 }else{
115                         if(m >= nbuf-1){
116                                 fprint(fdout, "line too long\n");
117                                 m = 0;
118                                 if(def != nil)
119                                         fprint(fdout, "%s[%s]: ", prompt, def);
120                                 else
121                                         fprint(fdout, "%s: ", prompt);
122                         }else
123                                 buf[m++] = line[0];
124                 }
125         }
126 }
127
128 typedef struct {
129         int     fd;
130         int     safelen;
131         vlong   safeoff;
132 } Nvrwhere;
133
134 static char *nvrfile = nil, *cputype = nil;
135
136 /* returns with *locp filled in and locp->fd open, if possible */
137 static void
138 findnvram(Nvrwhere *locp)
139 {
140         char *nvrlen, *nvroff, *v[2];
141         int fd, i, safelen;
142         vlong safeoff;
143
144         if (nvrfile == nil)
145                 nvrfile = getenv("nvram");
146         if (cputype == nil)
147                 cputype = getenv("cputype");
148         if(cputype == nil)
149                 cputype = strdup("mips");
150         if(strcmp(cputype, "386")==0 || strcmp(cputype, "amd64")==0 || strcmp(cputype, "alpha")==0) {
151                 free(cputype);
152                 cputype = strdup("pc");
153         }
154
155         fd = -1;
156         safeoff = -1;
157         safelen = -1;
158         if(nvrfile != nil && *nvrfile != '\0'){
159                 /* accept device and device!file */
160                 i = gettokens(nvrfile, v, nelem(v), "!");
161                 if (i < 1) {
162                         i = 1;
163                         v[0] = "";
164                         v[1] = nil;
165                 }
166                 fd = open(v[0], ORDWR);
167                 if (fd < 0)
168                         fd = open(v[0], OREAD);
169                 safelen = sizeof(Nvrsafe);
170                 if(strstr(v[0], "/9fat") == nil)
171                         safeoff = 0;
172                 nvrlen = getenv("nvrlen");
173                 if(nvrlen != nil)
174                         safelen = strtol(nvrlen, 0, 0);
175                 nvroff = getenv("nvroff");
176                 if(nvroff != nil)
177                         if(strcmp(nvroff, "dos") == 0)
178                                 safeoff = -1;
179                         else
180                                 safeoff = strtoll(nvroff, 0, 0);
181                 if(safeoff < 0 && fd >= 0){
182                         safelen = 512;
183                         safeoff = finddosfile(fd, i == 2? v[1]: "plan9.nvr");
184                         if(safeoff < 0){        /* didn't find plan9.nvr? */
185                                 close(fd);
186                                 fd = -1;
187                         }
188                 }
189                 free(nvroff);
190                 free(nvrlen);
191         }else
192                 for(i=0; i<nelem(nvtab); i++){
193                         if(strcmp(cputype, nvtab[i].cputype) != 0)
194                                 continue;
195                         if((fd = open(nvtab[i].file, ORDWR)) < 0)
196                                 continue;
197                         safeoff = nvtab[i].off;
198                         safelen = nvtab[i].len;
199                         if(safeoff == -1){
200                                 safeoff = finddosfile(fd, "plan9.nvr");
201                                 if(safeoff < 0){  /* didn't find plan9.nvr? */
202                                         close(fd);
203                                         fd = -1;
204                                         continue;
205                                 }
206                         }
207                         break;
208                 }
209         locp->fd = fd;
210         locp->safelen = safelen;
211         locp->safeoff = safeoff;
212 }
213
214 /*
215  *  get key info out of nvram.  since there isn't room in the PC's nvram use
216  *  a disk partition there.
217  */
218 int
219 readnvram(Nvrsafe *safep, int flag)
220 {
221         int err;
222         char buf[512], in[128];         /* 512 for floppy i/o */
223         Nvrsafe *safe;
224         Nvrwhere loc;
225
226         err = 0;
227         safe = (Nvrsafe*)buf;
228         memset(&loc, 0, sizeof loc);
229         findnvram(&loc);
230         if (loc.safelen < 0)
231                 loc.safelen = sizeof *safe;
232         else if (loc.safelen > sizeof buf)
233                 loc.safelen = sizeof buf;
234         if (loc.safeoff < 0) {
235                 fprint(2, "readnvram: couldn't find nvram\n");
236                 if(!(flag&NVwritemem))
237                         memset(safep, 0, sizeof(*safep));
238                 safe = safep;
239                 /*
240                  * allow user to type the data for authentication,
241                  * even if there's no nvram to store it in.
242                  */
243         }
244
245         if(flag&NVwritemem)
246                 safe = safep;
247         else {
248                 memset(safep, 0, sizeof(*safep));
249                 if(loc.fd < 0
250                 || seek(loc.fd, loc.safeoff, 0) < 0
251                 || read(loc.fd, buf, loc.safelen) != loc.safelen){
252                         err = 1;
253                         if(flag&(NVwrite|NVwriteonerr))
254                                 if(loc.fd < 0)
255                                         fprint(2, "can't open %s: %r\n", nvrfile);
256                                 else if (seek(loc.fd, loc.safeoff, 0) < 0)
257                                         fprint(2, "can't seek %s to %lld: %r\n",
258                                                 nvrfile, loc.safeoff);
259                                 else
260                                         fprint(2, "can't read %d bytes from %s: %r\n",
261                                                 loc.safelen, nvrfile);
262                         /* start from scratch */
263                         memset(safep, 0, sizeof(*safep));
264                         safe = safep;
265                 }else{
266                         *safep = *safe; /* overwrite arg with data read */
267                         safe = safep;
268
269                         /* verify data read */
270                         err |= check(safe->machkey, DESKEYLEN, safe->machsum,
271                                                 "bad nvram key");
272 //                      err |= check(safe->config, CONFIGLEN, safe->configsum,
273 //                                              "bad secstore key");
274                         err |= check(safe->authid, ANAMELEN, safe->authidsum,
275                                                 "bad authentication id");
276                         err |= check(safe->authdom, DOMLEN, safe->authdomsum,
277                                                 "bad authentication domain");
278                         if(err == 0)
279                                 if(safe->authid[0]==0 || safe->authdom[0]==0){
280                                         fprint(2, "empty nvram authid or authdom\n");
281                                         err = 1;
282                                 }
283                 }
284         }
285
286         if((flag&(NVwrite|NVwritemem)) || (err && (flag&NVwriteonerr))){
287                 if (!(flag&NVwritemem)) {
288                         readcons("authid", nil, 0, safe->authid,
289                                         sizeof safe->authid);
290                         readcons("authdom", nil, 0, safe->authdom,
291                                         sizeof safe->authdom);
292                         readcons("secstore key", nil, 1, safe->config,
293                                         sizeof safe->config);
294                         for(;;){
295                                 if(readcons("password", nil, 1, in, sizeof in)
296                                     == nil)
297                                         goto Out;
298                                 if(passtokey(safe->machkey, in))
299                                         break;
300                         }
301                 }
302
303                 // safe->authsum = nvcsum(safe->authkey, DESKEYLEN);
304                 safe->machsum = nvcsum(safe->machkey, DESKEYLEN);
305                 safe->configsum = nvcsum(safe->config, CONFIGLEN);
306                 safe->authidsum = nvcsum(safe->authid, sizeof safe->authid);
307                 safe->authdomsum = nvcsum(safe->authdom, sizeof safe->authdom);
308
309                 *(Nvrsafe*)buf = *safe;
310                 if(loc.fd < 0
311                 || seek(loc.fd, loc.safeoff, 0) < 0
312                 || write(loc.fd, buf, loc.safelen) != loc.safelen){
313                         fprint(2, "can't write key to nvram: %r\n");
314                         err = 1;
315                 }else
316                         err = 0;
317         }
318 Out:
319         if (loc.fd >= 0)
320                 close(loc.fd);
321         return err? -1: 0;
322 }
323
324 typedef struct Dosboot  Dosboot;
325 struct Dosboot{
326         uchar   magic[3];       /* really an xx86 JMP instruction */
327         uchar   version[8];
328         uchar   sectsize[2];
329         uchar   clustsize;
330         uchar   nresrv[2];
331         uchar   nfats;
332         uchar   rootsize[2];
333         uchar   volsize[2];
334         uchar   mediadesc;
335         uchar   fatsize[2];
336         uchar   trksize[2];
337         uchar   nheads[2];
338         uchar   nhidden[4];
339         uchar   bigvolsize[4];
340         uchar   driveno;
341         uchar   reserved0;
342         uchar   bootsig;
343         uchar   volid[4];
344         uchar   label[11];
345         uchar   type[8];
346 };
347 #define GETSHORT(p) (((p)[1]<<8) | (p)[0])
348 #define GETLONG(p) ((GETSHORT((p)+2) << 16) | GETSHORT((p)))
349
350 typedef struct Dosdir   Dosdir;
351 struct Dosdir
352 {
353         char    name[8];
354         char    ext[3];
355         uchar   attr;
356         uchar   reserved[10];
357         uchar   time[2];
358         uchar   date[2];
359         uchar   start[2];
360         uchar   length[4];
361 };
362
363 static char*
364 dosparse(char *from, char *to, int len)
365 {
366         char c;
367
368         memset(to, ' ', len);
369         if(from == 0)
370                 return 0;
371         while(len-- > 0){
372                 c = *from++;
373                 if(c == '.')
374                         return from;
375                 if(c == 0)
376                         break;
377                 if(c >= 'a' && c <= 'z')
378                         *to++ = c + 'A' - 'a';
379                 else
380                         *to++ = c;
381         }
382         return 0;
383 }
384
385 /*
386  *  return offset of first file block
387  *
388  *  This is a very simplistic dos file system.  It only
389  *  works on floppies, only looks in the root, and only
390  *  returns a pointer to the first block of a file.
391  *
392  *  This exists for cpu servers that have no hard disk
393  *  or nvram to store the key on.
394  *
395  *  Please don't make this any smarter: it stays resident
396  *  and I'ld prefer not to waste the space on something that
397  *  runs only at boottime -- presotto.
398  */
399 static long
400 finddosfile(int fd, char *file)
401 {
402         uchar secbuf[512];
403         char name[8];
404         char ext[3];
405         Dosboot *b;
406         Dosdir *root, *dp;
407         int nroot, sectsize, rootoff, rootsects, n;
408
409         /* dos'ize file name */
410         file = dosparse(file, name, 8);
411         dosparse(file, ext, 3);
412
413         /* read boot block, check for sanity */
414         b = (Dosboot*)secbuf;
415         if(read(fd, secbuf, sizeof(secbuf)) != sizeof(secbuf))
416                 return -1;
417         if(b->magic[0] != 0xEB || b->magic[1] != 0x3C || b->magic[2] != 0x90)
418                 return -1;
419         sectsize = GETSHORT(b->sectsize);
420         if(sectsize != 512)
421                 return -1;
422         rootoff = (GETSHORT(b->nresrv) + b->nfats*GETSHORT(b->fatsize)) * sectsize;
423         if(seek(fd, rootoff, 0) < 0)
424                 return -1;
425         nroot = GETSHORT(b->rootsize);
426         rootsects = (nroot*sizeof(Dosdir)+sectsize-1)/sectsize;
427         if(rootsects <= 0 || rootsects > 64)
428                 return -1;
429
430         /*
431          *  read root. it is contiguous to make stuff like
432          *  this easier
433          */
434         root = malloc(rootsects*sectsize);
435         if(read(fd, root, rootsects*sectsize) != rootsects*sectsize)
436                 return -1;
437         n = -1;
438         for(dp = root; dp < &root[nroot]; dp++)
439                 if(memcmp(name, dp->name, 8) == 0 && memcmp(ext, dp->ext, 3) == 0){
440                         n = GETSHORT(dp->start);
441                         break;
442                 }
443         free(root);
444
445         if(n < 0)
446                 return -1;
447
448         /*
449          *  dp->start is in cluster units, not sectors.  The first
450          *  cluster is cluster 2 which starts immediately after the
451          *  root directory
452          */
453         return rootoff + rootsects*sectsize + (n-2)*sectsize*b->clustsize;
454 }
455