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