xref: /plan9-contrib/sys/src/libauthsrv/readnvram.c (revision 9b943567965ba040fd275927fbe088656eb8ce4f)
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/sd00/nvram", 0, sizeof(Nvrsafe),
33 	"pc", "#S/sd00/9fat", -1, sizeof(Nvrsafe),
34 	"pc", "#S/sd01/nvram", 0, sizeof(Nvrsafe),
35 	"pc", "#S/sd01/9fat", -1, sizeof(Nvrsafe),
36 	"pc", "#f/fd0disk", -1, 512,	/* 512: #f requires whole sector reads */
37 	"pc", "#f/fd1disk", -1, 512,
38 	"mips", "#r/nvram", 1024+900, sizeof(Nvrsafe),
39 	"power", "#F/flash/flash0", 0x440000, sizeof(Nvrsafe),
40 	"power", "#r/nvram", 4352, sizeof(Nvrsafe),	/* OK for MTX-604e */
41 	"debug", "/tmp/nvram", 0, sizeof(Nvrsafe),
42 };
43 
44 static char*
45 readcons(char *prompt, char *def, int raw, char *buf, int nbuf)
46 {
47 	int fdin, fdout, ctl, n, m;
48 	char line[10];
49 
50 	fdin = open("/dev/cons", OREAD);
51 	if(fdin < 0)
52 		fdin = 0;
53 	fdout = open("/dev/cons", OWRITE);
54 	if(fdout < 0)
55 		fdout = 1;
56 	if(def != nil)
57 		fprint(fdout, "%s[%s]: ", prompt, def);
58 	else
59 		fprint(fdout, "%s: ", prompt);
60 	if(raw){
61 		ctl = open("/dev/consctl", OWRITE);
62 		if(ctl >= 0)
63 			write(ctl, "rawon", 5);
64 	} else
65 		ctl = -1;
66 
67 	m = 0;
68 	for(;;){
69 		n = read(fdin, line, 1);
70 		if(n == 0){
71 			close(ctl);
72 			werrstr("readcons: EOF");
73 			return nil;
74 		}
75 		if(n < 0){
76 			close(ctl);
77 			werrstr("can't read cons");
78 			return nil;
79 		}
80 		if(line[0] == 0x7f)
81 			exits(0);
82 		if(n == 0 || line[0] == '\n' || line[0] == '\r'){
83 			if(raw){
84 				write(ctl, "rawoff", 6);
85 				write(fdout, "\n", 1);
86 				close(ctl);
87 			}
88 			buf[m] = '\0';
89 			if(buf[0]=='\0' && def)
90 				strcpy(buf, def);
91 			return buf;
92 		}
93 		if(line[0] == '\b'){
94 			if(m > 0)
95 				m--;
96 		}else if(line[0] == 0x15){	/* ^U: line kill */
97 			m = 0;
98 			if(def != nil)
99 				fprint(fdout, "%s[%s]: ", prompt, def);
100 			else
101 				fprint(fdout, "%s: ", prompt);
102 		}else{
103 			if(m >= nbuf-1){
104 				fprint(fdout, "line too long\n");
105 				m = 0;
106 				if(def != nil)
107 					fprint(fdout, "%s[%s]: ", prompt, def);
108 				else
109 					fprint(fdout, "%s: ", prompt);
110 			}else
111 				buf[m++] = line[0];
112 		}
113 	}
114 	return buf;	/* how does this happen */
115 }
116 
117 
118 /*
119  *  get key info out of nvram.  since there isn't room in the PC's nvram use
120  *  a disk partition there.
121  */
122 int
123 readnvram(Nvrsafe *safep, int flag)
124 {
125 	char buf[1024], in[128], *cputype, *nvrfile, *nvrlen, *nvroff, *v[2];
126 	int fd, err, i, safeoff, safelen;
127 	Nvrsafe *safe;
128 
129 	err = 0;
130 	memset(safep, 0, sizeof(*safep));
131 
132 	nvrfile = getenv("nvram");
133 	cputype = getenv("cputype");
134 	if(cputype == nil)
135 		cputype = "mips";
136 	if(strcmp(cputype, "386")==0 || strcmp(cputype, "alpha")==0)
137 		cputype = "pc";
138 
139 	safe = (Nvrsafe*)buf;
140 
141 	fd = -1;
142 	safeoff = -1;
143 	safelen = -1;
144 	if(nvrfile != nil){
145 		/* accept device and device!file */
146 		i = gettokens(nvrfile, v, nelem(v), "!");
147 		fd = open(v[0], ORDWR);
148 		safelen = sizeof(Nvrsafe);
149 		if(strstr(v[0], "/9fat") == nil)
150 			safeoff = 0;
151 		nvrlen = getenv("nvrlen");
152 		if(nvrlen != nil)
153 			safelen = atoi(nvrlen);
154 		nvroff = getenv("nvroff");
155 		if(nvroff != nil){
156 			if(strcmp(nvroff, "dos") == 0)
157 				safeoff = -1;
158 			else
159 				safeoff = atoi(nvroff);
160 		}
161 		if(safeoff < 0 && fd >= 0){
162 			safelen = 512;
163 			safeoff = finddosfile(fd, i == 2 ? v[1] : "plan9.nvr");
164 			if(safeoff < 0){
165 				close(fd);
166 				fd = -1;
167 			}
168 		}
169 		free(nvrfile);
170 		if(nvrlen != nil)
171 			free(nvrlen);
172 		if(nvroff != nil)
173 			free(nvroff);
174 	}else{
175 		for(i=0; i<nelem(nvtab); i++){
176 			if(strcmp(cputype, nvtab[i].cputype) != 0)
177 				continue;
178 			if((fd = open(nvtab[i].file, ORDWR)) < 0)
179 				continue;
180 			safeoff = nvtab[i].off;
181 			safelen = nvtab[i].len;
182 			if(safeoff == -1){
183 				safeoff = finddosfile(fd, "plan9.nvr");
184 				if(safeoff < 0){
185 					close(fd);
186 					fd = -1;
187 					continue;
188 				}
189 			}
190 			break;
191 		}
192 	}
193 
194 	if(fd < 0
195 	|| seek(fd, safeoff, 0) < 0
196 	|| read(fd, buf, safelen) != safelen){
197 		err = 1;
198 		if(flag&(NVwrite|NVwriteonerr))
199 			fprint(2, "can't read nvram: %r\n");
200 		memset(safep, 0, sizeof(*safep));
201 		safe = safep;
202 	}else{
203 		*safep = *safe;
204 		safe = safep;
205 
206 		err |= check(safe->machkey, DESKEYLEN, safe->machsum, "bad nvram key");
207 //		err |= check(safe->config, CONFIGLEN, safe->configsum, "bad secstore key");
208 		err |= check(safe->authid, ANAMELEN, safe->authidsum, "bad authentication id");
209 		err |= check(safe->authdom, DOMLEN, safe->authdomsum, "bad authentication domain");
210 	}
211 
212 	if((flag&NVwrite) || (err && (flag&NVwriteonerr))){
213 		readcons("authid", nil, 0, safe->authid, sizeof(safe->authid));
214 		readcons("authdom", nil, 0, safe->authdom, sizeof(safe->authdom));
215 		readcons("secstore key", nil, 1, safe->config, sizeof(safe->config));
216 		for(;;){
217 			if(readcons("password", nil, 1, in, sizeof in) == nil)
218 				goto Out;
219 			if(passtokey(safe->machkey, in))
220 				break;
221 		}
222 		safe->machsum = nvcsum(safe->machkey, DESKEYLEN);
223 		safe->configsum = nvcsum(safe->config, CONFIGLEN);
224 		safe->authidsum = nvcsum(safe->authid, sizeof(safe->authid));
225 		safe->authdomsum = nvcsum(safe->authdom, sizeof(safe->authdom));
226 		*(Nvrsafe*)buf = *safe;
227 		if(seek(fd, safeoff, 0) < 0
228 		|| write(fd, buf, safelen) != safelen){
229 			fprint(2, "can't write key to nvram: %r\n");
230 			err = 1;
231 		}else
232 			err = 0;
233 	}
234 Out:
235 	close(fd);
236 	return err ? -1 : 0;
237 }
238 
239 typedef struct Dosboot	Dosboot;
240 struct Dosboot{
241 	uchar	magic[3];	/* really an xx86 JMP instruction */
242 	uchar	version[8];
243 	uchar	sectsize[2];
244 	uchar	clustsize;
245 	uchar	nresrv[2];
246 	uchar	nfats;
247 	uchar	rootsize[2];
248 	uchar	volsize[2];
249 	uchar	mediadesc;
250 	uchar	fatsize[2];
251 	uchar	trksize[2];
252 	uchar	nheads[2];
253 	uchar	nhidden[4];
254 	uchar	bigvolsize[4];
255 	uchar	driveno;
256 	uchar	reserved0;
257 	uchar	bootsig;
258 	uchar	volid[4];
259 	uchar	label[11];
260 	uchar	type[8];
261 };
262 #define	GETSHORT(p) (((p)[1]<<8) | (p)[0])
263 #define	GETLONG(p) ((GETSHORT((p)+2) << 16) | GETSHORT((p)))
264 
265 typedef struct Dosdir	Dosdir;
266 struct Dosdir
267 {
268 	char	name[8];
269 	char	ext[3];
270 	uchar	attr;
271 	uchar	reserved[10];
272 	uchar	time[2];
273 	uchar	date[2];
274 	uchar	start[2];
275 	uchar	length[4];
276 };
277 
278 static char*
279 dosparse(char *from, char *to, int len)
280 {
281 	char c;
282 
283 	memset(to, ' ', len);
284 	if(from == 0)
285 		return 0;
286 	while(len-- > 0){
287 		c = *from++;
288 		if(c == '.')
289 			return from;
290 		if(c == 0)
291 			break;
292 		if(c >= 'a' && c <= 'z')
293 			*to++ = c + 'A' - 'a';
294 		else
295 			*to++ = c;
296 	}
297 	return 0;
298 }
299 
300 /*
301  *  return offset of first file block
302  *
303  *  This is a very simplistic dos file system.  It only
304  *  works on floppies, only looks in the root, and only
305  *  returns a pointer to the first block of a file.
306  *
307  *  This exists for cpu servers that have no hard disk
308  *  or nvram to store the key on.
309  *
310  *  Please don't make this any smarter: it stays resident
311  *  and I'ld prefer not to waste the space on something that
312  *  runs only at boottime -- presotto.
313  */
314 static long
315 finddosfile(int fd, char *file)
316 {
317 	uchar secbuf[512];
318 	char name[8];
319 	char ext[3];
320 	Dosboot	*b;
321 	Dosdir *root, *dp;
322 	int nroot, sectsize, rootoff, rootsects, n;
323 
324 	/* dos'ize file name */
325 	file = dosparse(file, name, 8);
326 	dosparse(file, ext, 3);
327 
328 	/* read boot block, check for sanity */
329 	b = (Dosboot*)secbuf;
330 	if(read(fd, secbuf, sizeof(secbuf)) != sizeof(secbuf))
331 		return -1;
332 	if(b->magic[0] != 0xEB || b->magic[1] != 0x3C || b->magic[2] != 0x90)
333 		return -1;
334 	sectsize = GETSHORT(b->sectsize);
335 	if(sectsize != 512)
336 		return -1;
337 	rootoff = (GETSHORT(b->nresrv) + b->nfats*GETSHORT(b->fatsize)) * sectsize;
338 	if(seek(fd, rootoff, 0) < 0)
339 		return -1;
340 	nroot = GETSHORT(b->rootsize);
341 	rootsects = (nroot*sizeof(Dosdir)+sectsize-1)/sectsize;
342 	if(rootsects <= 0 || rootsects > 64)
343 		return -1;
344 
345 	/*
346 	 *  read root. it is contiguous to make stuff like
347 	 *  this easier
348 	 */
349 	root = malloc(rootsects*sectsize);
350 	if(read(fd, root, rootsects*sectsize) != rootsects*sectsize)
351 		return -1;
352 	n = -1;
353 	for(dp = root; dp < &root[nroot]; dp++)
354 		if(memcmp(name, dp->name, 8) == 0 && memcmp(ext, dp->ext, 3) == 0){
355 			n = GETSHORT(dp->start);
356 			break;
357 		}
358 	free(root);
359 
360 	if(n < 0)
361 		return -1;
362 
363 	/*
364 	 *  dp->start is in cluster units, not sectors.  The first
365 	 *  cluster is cluster 2 which starts immediately after the
366 	 *  root directory
367 	 */
368 	return rootoff + rootsects*sectsize + (n-2)*sectsize*b->clustsize;
369 }
370 
371