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