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