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))
41typedef union {
42	char buf[BUFSIZE];
43	struct openpromio opp;
44} Oppbuf;
45
46extern int _error(int do_perror, char *fmt, ...);
47extern void setpname(char *);
48static int get_password(char *, int);
49extern int loadlogo(char *, int, int, char *);
50
51#define	NO_PERROR	0
52#define	PERROR		1
53
54static int prom_fd;
55static char *promdev;
56static int verbose;
57
58static void do_var(char *);
59static void dump_all();
60static void print_one(char *);
61static void set_one(char *, char *);
62static void promclose();
63static int promopen(int);
64
65static int getpropval(struct openpromio *);
66static int setpropval(struct openpromio *);
67
68static char *badarchmsg = "Architecture does not support this command.\n";
69
70typedef	void (*func)();
71
72
73/* We have to special-case two properties related to security */
74static void i_secure();
75static void i_passwd(), o_passwd();
76static 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 */
93static 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
111int
112main(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 */
184static void
185do_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 */
218static void
219dump_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 */
251static void
252print_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 */
296static void
297set_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
341static int
342promopen(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
359static void
360promclose()
361{
362	if (close(prom_fd) < 0)
363		exit(_error(PERROR, "close error on %s", promdev));
364}
365
366static int
367getpropval(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
377static int
378setpropval(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
392static void
393i_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 */
413static void
414i_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 */
482static void
483i_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 */
517static void
518o_passwd(char *var, char *val)
519{
520	/* Don't print the password */
521}
522
523static int
524get_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
532tryagain:
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