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