xref: /illumos-gate/usr/src/cmd/eeprom/sparc/openprom.c (revision 70f9559bd0c02885d84a425eaafc8c280df10efb)
1 /*
2  * Open Boot Prom eeprom utility
3  */
4 
5 /*
6  * Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved.
7  */
8 
9 /*
10  * Copyright (c) 1983 Regents of the University of California.
11  * All rights reserved. The Berkeley software License Agreement
12  * specifies the terms and conditions for redistribution.
13  */
14 
15 #include <sys/types.h>
16 #include <sys/param.h>
17 #include <sys/openpromio.h>
18 #include <stdio.h>
19 #include <fcntl.h>
20 #include <string.h>
21 #include <errno.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 
25 /*
26  * Usage:  % eeprom [-v] [-f promdev] [-]
27  *	   % eeprom [-v] [-f promdev] field[=value] ...
28  */
29 
30 /*
31  * 128 is the size of the largest (currently) property name buffer
32  * 8192 - MAXPROPSIZE - sizeof (int) is the size of the largest
33  * (currently) property value, viz. nvramrc.
34  * the sizeof(u_int) is from struct openpromio
35  */
36 
37 #define	MAXPROPSIZE	128
38 #define	MAXNAMESIZE	MAXPROPSIZE
39 #define	MAXVALSIZE	(8192 - MAXPROPSIZE - sizeof (uint_t))
40 #define	BUFSIZE		(MAXPROPSIZE + MAXVALSIZE + sizeof (uint_t))
41 typedef union {
42 	char buf[BUFSIZE];
43 	struct openpromio opp;
44 } Oppbuf;
45 
46 extern int _error(int do_perror, char *fmt, ...);
47 extern void setpname(char *);
48 static int get_password(char *, int);
49 extern int loadlogo(char *, int, int, char *);
50 
51 #define	NO_PERROR	0
52 #define	PERROR		1
53 
54 static int prom_fd;
55 static char *promdev;
56 static int verbose;
57 
58 static void do_var(char *);
59 static void dump_all();
60 static void print_one(char *);
61 static void set_one(char *, char *);
62 static void promclose();
63 static int promopen(int);
64 
65 static int getpropval(struct openpromio *);
66 static int setpropval(struct openpromio *);
67 
68 static char *badarchmsg = "Architecture does not support this command.\n";
69 
70 typedef	void (*func)();
71 
72 
73 /* We have to special-case two properties related to security */
74 static void i_secure();
75 static void i_passwd(), o_passwd();
76 static void i_oemlogo();
77 
78 /*
79  * It's unfortunate that we have to know the names of certain properties
80  * in this program (the whole idea of openprom was to avoid it), but at
81  * least we can isolate them to these defines here.
82  */
83 #define	PASSWORD_PROPERTY "security-password"
84 #define	MODE_PROPERTY "security-mode"
85 #define	LOGO_PROPERTY "oem-logo"
86 #define	PW_SIZE 8
87 
88 /*
89  * Unlike the old-style eeprom command, where every property needed an
90  * i_foo and an o_foo function, we only need them when the default case
91  * isn't sufficient.
92  */
93 static struct	opvar {
94 	char	*name;
95 	func	in;
96 	func	out;
97 } opvar[] = {
98 #define	e(n, i, o)	{n, i, o}
99 	e(MODE_PROPERTY,	i_secure,	(func)NULL),
100 	e(PASSWORD_PROPERTY,	i_passwd,	o_passwd),
101 	e(LOGO_PROPERTY,	i_oemlogo,	(func)NULL),
102 	{ (char *)NULL, (func)NULL, (func)NULL}
103 #undef e
104 };
105 
106 
107 /*
108  * sun4c openprom
109  */
110 
111 int
112 main(int argc, char **argv)
113 {
114 	int c;
115 	extern char *optarg;
116 	extern int optind;
117 
118 	promdev = "/dev/openprom";
119 
120 	while ((c = getopt(argc, argv, "cif:v")) != -1)
121 		switch (c) {
122 		case 'c':
123 		case 'i':
124 			/* ignore for openprom */
125 			break;
126 		case 'v':
127 			verbose++;
128 			break;
129 		case 'f':
130 			promdev = optarg;
131 			break;
132 		default:
133 			exit(_error(NO_PERROR,
134 			    "Usage: %s [-v] [-f prom-device] "
135 			    "[variable[=value] ...]", argv[0]));
136 		}
137 
138 	setpname(argv[0]);
139 
140 	/*
141 	 * If no arguments, dump all fields.
142 	 */
143 	if (optind >= argc) {
144 		dump_all();
145 		exit(0);
146 	}
147 
148 	while (optind < argc) {
149 		/*
150 		 * If "-" specified, read variables from stdin.
151 		 */
152 		if (strcmp(argv[optind], "-") == 0) {
153 			int c;
154 			char *nl, line[BUFSIZE];
155 
156 			while (fgets(line, sizeof (line), stdin) != NULL) {
157 				/* zap newline if present */
158 				if (nl = strchr(line, '\n'))
159 					*nl = 0;
160 				/* otherwise discard rest of line */
161 				else
162 					while ((c = getchar()) != '\n' &&
163 					    c != EOF)
164 						/* nothing */;
165 
166 				do_var(line);
167 			}
168 			clearerr(stdin);
169 		}
170 		/*
171 		 * Process each argument as a variable print or set request.
172 		 */
173 		else
174 			do_var(argv[optind]);
175 
176 		optind++;
177 	}
178 	return (0);
179 }
180 
181 /*
182  * Print or set an EEPROM field.
183  */
184 static void
185 do_var(char *var)
186 {
187 	char *val;
188 
189 	val = strchr(var, '=');
190 
191 	if (val == NULL) {
192 		/*
193 		 * print specific property
194 		 */
195 		if (promopen(O_RDONLY))  {
196 			(void) fprintf(stderr, badarchmsg);
197 			exit(1);
198 		}
199 		print_one(var);
200 	} else {
201 		/*
202 		 * set specific property to value
203 		 */
204 		*val++ = '\0';
205 
206 		if (promopen(O_RDWR))  {
207 			(void) fprintf(stderr, badarchmsg);
208 			exit(1);
209 		}
210 		set_one(var, val);
211 	}
212 	promclose();
213 }
214 
215 /*
216  * Print all properties and values
217  */
218 static void
219 dump_all()
220 {
221 	Oppbuf	oppbuf;
222 	struct openpromio *opp = &(oppbuf.opp);
223 
224 	if (promopen(O_RDONLY))  {
225 		(void) fprintf(stderr, badarchmsg);
226 		exit(1);
227 	}
228 	/* get first prop by asking for null string */
229 	(void) memset(oppbuf.buf, '\0', BUFSIZE);
230 	/* CONSTCOND */
231 	while (1) {
232 		/*
233 		 * get property
234 		 */
235 		opp->oprom_size = MAXPROPSIZE;
236 
237 		if (ioctl(prom_fd, OPROMNXTOPT, opp) < 0)
238 			exit(_error(PERROR, "OPROMNXTOPT"));
239 
240 		if (opp->oprom_size == 0) {
241 			promclose();
242 			return;
243 		}
244 		print_one(opp->oprom_array);
245 	}
246 }
247 
248 /*
249  * Print one property and its value.
250  */
251 static void
252 print_one(char *var)
253 {
254 	Oppbuf	oppbuf;
255 	struct openpromio *opp = &(oppbuf.opp);
256 	char bootargs[MAXVALSIZE];
257 
258 	if (strcmp(var, "bootcmd") == 0) {
259 		opp->oprom_size = MAXVALSIZE;
260 		if (ioctl(prom_fd, OPROMGETBOOTARGS, opp) < 0) {
261 			(void) _error(PERROR, "OPROMGETBOOTARGS");
262 			return;
263 		}
264 		(void) strlcpy(bootargs, opp->oprom_array, MAXVALSIZE);
265 
266 		opp->oprom_size = MAXVALSIZE;
267 		if (ioctl(prom_fd, OPROMGETBOOTPATH, opp) < 0) {
268 			(void) _error(PERROR, "OPROMGETBOOTPATH");
269 			return;
270 		}
271 		(void) printf("%s=%s %s\n", var, opp->oprom_array, bootargs);
272 		return;
273 	}
274 
275 	(void) strlcpy(opp->oprom_array, var, MAXNAMESIZE);
276 	if (getpropval(opp) || opp->oprom_size <= 0)
277 		(void) printf("%s: data not available.\n", var);
278 	else {
279 		/* If necessary, massage the output */
280 		struct opvar *v;
281 
282 		for (v = opvar; v->name; v++)
283 			if (strcmp(var, v->name) == 0)
284 				break;
285 
286 		if (v->name && v->out)
287 			(*v->out)(v->name, opp->oprom_array);
288 		else
289 			(void) printf("%s=%s\n", var, opp->oprom_array);
290 	}
291 }
292 
293 /*
294  * Set one property to the given value.
295  */
296 static void
297 set_one(char *var, char *val)
298 {
299 	Oppbuf	oppbuf;
300 	struct openpromio *opp = &(oppbuf.opp);
301 	struct opvar *v;
302 
303 	if (verbose) {
304 		(void) printf("old:");
305 		print_one(var);
306 	}
307 
308 	/* If necessary, massage the input */
309 
310 	for (v = opvar; v->name; v++)
311 		if (strcmp(var, v->name) == 0)
312 			break;
313 
314 	if (v->name && v->in)
315 		(*v->in)(v->name, val, opp);
316 	else {
317 		int varlen = strlen(var) + 1;
318 		int vallen = strlen(val);
319 
320 		if (varlen > MAXNAMESIZE) {
321 			(void) printf("%s: invalid property.\n", var);
322 			return;
323 		}
324 		if (vallen >= MAXVALSIZE) {
325 			(void) printf("%s: invalid property value.\n", var);
326 			return;
327 		}
328 		(void) strcpy(opp->oprom_array, var);
329 		(void) strcpy(opp->oprom_array + varlen, val);
330 		opp->oprom_size = varlen + vallen;
331 		if (setpropval(opp))
332 			(void) printf("%s: invalid property.\n", var);
333 	}
334 
335 	if (verbose) {
336 		(void) printf("new:");
337 		print_one(var);
338 	}
339 }
340 
341 static int
342 promopen(int oflag)
343 {
344 	/* CONSTCOND */
345 	while (1)  {
346 		if ((prom_fd = open(promdev, oflag)) < 0)  {
347 			if (errno == EAGAIN)
348 				continue;
349 			else if (errno == ENXIO)
350 				return (-1);
351 			else
352 				exit(_error(PERROR, "cannot open %s", promdev));
353 		} else
354 			break;
355 	}
356 	return (0);
357 }
358 
359 static void
360 promclose()
361 {
362 	if (close(prom_fd) < 0)
363 		exit(_error(PERROR, "close error on %s", promdev));
364 }
365 
366 static int
367 getpropval(struct openpromio *opp)
368 {
369 	opp->oprom_size = MAXVALSIZE;
370 
371 	if (ioctl(prom_fd, OPROMGETOPT, opp) < 0)
372 		return (_error(PERROR, "OPROMGETOPT"));
373 
374 	return (0);
375 }
376 
377 static int
378 setpropval(struct openpromio *opp)
379 {
380 	/* Caller must set opp->oprom_size */
381 
382 	if (ioctl(prom_fd, OPROMSETOPT, opp) < 0)
383 		return (_error(PERROR, "OPROMSETOPT"));
384 	return (0);
385 }
386 
387 
388 /*
389  * The next set of functions handle the special cases.
390  */
391 
392 static void
393 i_oemlogo(char *var, char *val, struct openpromio *opp)
394 {
395 	int varlen = strlen(var) + 1;
396 
397 	(void) strcpy(opp->oprom_array, var);	/* safe - we know the name */
398 
399 	if (loadlogo(val, 64, 64, opp->oprom_array + varlen))
400 		exit(1);
401 	opp->oprom_size = varlen + 512;
402 	if (ioctl(prom_fd, OPROMSETOPT2, opp) < 0)
403 		exit(_error(PERROR, "OPROMSETOPT2"));
404 }
405 
406 /*
407  * Set security mode.
408  * If oldmode was none, and new mode is not none, get and set password,
409  * too.
410  * If old mode was not none, and new mode is none, wipe out old
411  * password.
412  */
413 static void
414 i_secure(char *var, char *val, struct openpromio *opp)
415 {
416 	int secure;
417 	Oppbuf oppbuf;
418 	struct openpromio *opp2 = &(oppbuf.opp);
419 	char pwbuf[PW_SIZE + 2];
420 	int varlen1, varlen2;
421 
422 	(void) strcpy(opp2->oprom_array, var);	/* safe; we know the name */
423 	if (getpropval(opp2) || opp2->oprom_size <= 0) {
424 		(void) printf("%s: data not available.\n", var);
425 		exit(1);
426 	}
427 	secure = strcmp(opp2->oprom_array, "none");
428 
429 	/* Set up opp for mode */
430 	(void) strcpy(opp->oprom_array, var);	/* safe; we know the name */
431 	varlen1 = strlen(opp->oprom_array) + 1;
432 	if (strlen(val) > 32) {		/* 32 > [ "full", "command", "none" ] */
433 		(void) printf("Invalid security mode, mode unchanged.\n");
434 		exit(1);
435 	}
436 	(void) strcpy(opp->oprom_array + varlen1, val);
437 	opp->oprom_size = varlen1 + strlen(val);
438 
439 	/* Set up opp2 for password */
440 	(void) strcpy(opp2->oprom_array, PASSWORD_PROPERTY);
441 	varlen2 = strlen(opp2->oprom_array) + 1;
442 
443 	if ((strcmp(val, "full") == 0) || (strcmp(val, "command") == 0)) {
444 		if (! secure) {
445 			/* no password yet, get one */
446 			if (get_password(pwbuf, PW_SIZE)) {
447 				(void) strcpy(opp2->oprom_array + varlen2,
448 				    pwbuf);
449 				opp2->oprom_size = varlen2 + strlen(pwbuf);
450 				/* set password first */
451 				if (setpropval(opp2) || setpropval(opp))
452 					exit(1);
453 			} else
454 				exit(1);
455 		} else {
456 			if (setpropval(opp))
457 				exit(1);
458 		}
459 	} else if (strcmp(val, "none") == 0) {
460 		if (secure) {
461 			(void) memset(opp2->oprom_array + varlen2, '\0',
462 			    PW_SIZE);
463 			opp2->oprom_size = varlen2 + PW_SIZE;
464 			/* set mode first */
465 			if (setpropval(opp) || setpropval(opp2))
466 				exit(1);
467 		} else {
468 			if (setpropval(opp))
469 				exit(1);
470 		}
471 	} else {
472 		(void) printf("Invalid security mode, mode unchanged.\n");
473 		exit(1);
474 	}
475 }
476 
477 /*
478  * Set password.
479  * We must be in a secure mode in order to do this.
480  */
481 /* ARGSUSED */
482 static void
483 i_passwd(char *var, char *val, struct openpromio *opp)
484 {
485 	int secure;
486 	Oppbuf oppbuf;
487 	struct openpromio *opp2 = &(oppbuf.opp);
488 	char pwbuf[PW_SIZE + 2];
489 	int varlen;
490 
491 	(void) strcpy(opp2->oprom_array, MODE_PROPERTY);
492 	if (getpropval(opp2) || opp2->oprom_size <= 0) {
493 		(void) printf("%s: data not available.\n", opp2->oprom_array);
494 		exit(1);
495 	}
496 	secure = strcmp(opp2->oprom_array, "none");
497 
498 	if (!secure) {
499 		(void) printf("Not in secure mode\n");
500 		exit(1);
501 	}
502 
503 	/* Set up opp for password */
504 	(void) strcpy(opp->oprom_array, var);	/* Safe; We know the name */
505 	varlen = strlen(opp->oprom_array) + 1;
506 
507 	if (get_password(pwbuf, PW_SIZE)) {
508 		(void) strcpy(opp->oprom_array + varlen, pwbuf); /* Bounded */
509 		opp->oprom_size = varlen + strlen(pwbuf);
510 		if (setpropval(opp))
511 			exit(1);
512 	} else
513 		exit(1);
514 }
515 
516 /* ARGSUSED */
517 static void
518 o_passwd(char *var, char *val)
519 {
520 	/* Don't print the password */
521 }
522 
523 static int
524 get_password(char *pw_dest, int pwsize)
525 {
526 	int insist = 0, ok, flags;
527 	int c, pwlen;
528 	char *p;
529 	static char pwbuf[256];
530 	char *pasword = NULL;
531 
532 tryagain:
533 	(void) printf("Changing PROM password:\n");
534 	if ((pasword = getpass("New password:")) == NULL) {
535 		exit(_error(NO_PERROR, "failed to get password"));
536 	}
537 	(void) strcpy(pwbuf, pasword);
538 	pwlen = strlen(pwbuf);
539 	if (pwlen == 0) {
540 		(void) printf("Password unchanged.\n");
541 		return (0);
542 	}
543 	/*
544 	 * Insure password is of reasonable length and
545 	 * composition.  If we really wanted to make things
546 	 * sticky, we could check the dictionary for common
547 	 * words, but then things would really be slow.
548 	 */
549 	ok = 0;
550 	flags = 0;
551 	p = pwbuf;
552 	while ((c = *p++) != 0) {
553 		if (c >= 'a' && c <= 'z')
554 			flags |= 2;
555 		else if (c >= 'A' && c <= 'Z')
556 			flags |= 4;
557 		else if (c >= '0' && c <= '9')
558 			flags |= 1;
559 		else
560 			flags |= 8;
561 	}
562 	if (flags >= 7 && pwlen >= 4)
563 		ok = 1;
564 	if ((flags == 2 || flags == 4) && pwlen >= 6)
565 		ok = 1;
566 	if ((flags == 3 || flags == 5 || flags == 6) && pwlen >= 5)
567 		ok = 1;
568 	if (!ok && insist < 2) {
569 	(void) printf("Please use %s.\n", flags == 1 ?
570 	    "at least one non-numeric character" : "a longer password");
571 		insist++;
572 		goto tryagain;
573 	}
574 	if (strcmp(pwbuf, getpass("Retype new password:")) != 0) {
575 		(void) printf("Mismatch - password unchanged.\n");
576 		return (0);
577 	}
578 	(void) strncpy(pw_dest, pwbuf, pwsize);
579 	return (1);
580 }
581