]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/binmiscctl/binmiscctl.c
Followup to r347996
[FreeBSD/FreeBSD.git] / usr.sbin / binmiscctl / binmiscctl.c
1 /*-
2  * Copyright (c) 2013 Stacey D. Son
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  */
27
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30
31 #include <ctype.h>
32 #include <errno.h>
33 #include <getopt.h>
34 #include <stdio.h>
35 #include <stdarg.h>
36 #include <stdint.h>
37 #include <stdlib.h>
38 #include <string.h>
39
40 #include <sys/types.h>
41 #include <sys/imgact_binmisc.h>
42 #include <sys/linker.h>
43 #include <sys/module.h>
44 #include <sys/sysctl.h>
45
46 enum cmd {
47         CMD_ADD = 0,
48         CMD_REMOVE,
49         CMD_DISABLE,
50         CMD_ENABLE,
51         CMD_LOOKUP,
52         CMD_LIST,
53 };
54
55 extern char *__progname;
56
57 typedef int (*cmd_func_t)(int argc, char *argv[], ximgact_binmisc_entry_t *xbe);
58
59 int add_cmd(int argc, char *argv[], ximgact_binmisc_entry_t *xbe);
60 int name_cmd(int argc, char *argv[], ximgact_binmisc_entry_t *xbe);
61 int noname_cmd(int argc, char *argv[], ximgact_binmisc_entry_t *xbe);
62
63 static const struct {
64         const int token;
65         const char *name;
66         cmd_func_t func;
67         const char *desc;
68         const char *args;
69 } cmds[] = {
70         {
71                 CMD_ADD,
72                 "add",
73                 add_cmd,
74                 "Add a new binary image activator (requires 'root' privilege)",
75                 "<name> --interpreter <path_and_arguments> \\\n"
76                 "\t\t--magic <magic_bytes> [--mask <mask_bytes>] \\\n"
77                 "\t\t--size <magic_size> [--offset <magic_offset>] \\\n"
78                 "\t\t[--set-enabled]"
79         },
80         {
81                 CMD_REMOVE,
82                 "remove",
83                 name_cmd,
84                 "Remove a binary image activator (requires 'root' privilege)",
85                 "<name>"
86         },
87         {
88                 CMD_DISABLE,
89                 "disable",
90                 name_cmd,
91                 "Disable a binary image activator (requires 'root' privilege)",
92                 "<name>"
93         },
94         {
95                 CMD_ENABLE,
96                 "enable",
97                 name_cmd,
98                 "Enable a binary image activator (requires 'root' privilege)",
99                 "<name>"
100         },
101         {
102                 CMD_LOOKUP,
103                 "lookup",
104                 name_cmd,
105                 "Lookup a binary image activator",
106                 "<name>"
107         },
108         {
109                 CMD_LIST,
110                 "list",
111                 noname_cmd,
112                 "List all the binary image activators",
113                 ""
114         },
115 };
116
117 static const struct option
118 add_opts[] = {
119         { "set-enabled",        no_argument,            NULL,   'e' },
120         { "interpreter",        required_argument,      NULL,   'i' },
121         { "mask",               required_argument,      NULL,   'M' },
122         { "magic",              required_argument,      NULL,   'm' },
123         { "offset",             required_argument,      NULL,   'o' },
124         { "size",               required_argument,      NULL,   's' },
125         { NULL,                 0,                      NULL,   0   }
126 };
127
128 static char const *cmd_sysctl_name[] = {
129         IBE_SYSCTL_NAME_ADD,
130         IBE_SYSCTL_NAME_REMOVE,
131         IBE_SYSCTL_NAME_DISABLE,
132         IBE_SYSCTL_NAME_ENABLE,
133         IBE_SYSCTL_NAME_LOOKUP,
134         IBE_SYSCTL_NAME_LIST
135 };
136
137 static void
138 usage(const char *format, ...)
139 {
140         va_list args;
141         size_t i;
142         int error = 0;
143
144         va_start(args, format);
145         if (format) {
146                 vfprintf(stderr, format, args);
147                 error = -1;
148         }
149         va_end(args);
150         fprintf(stderr, "\n");
151         fprintf(stderr, "usage: %s command [args...]\n\n", __progname);
152
153         for(i = 0; i < ( sizeof (cmds) / sizeof (cmds[0])); i++) {
154                 fprintf(stderr, "%s:\n", cmds[i].desc);
155                 fprintf(stderr, "\t%s %s %s\n\n", __progname, cmds[i].name,
156                     cmds[i].args);
157         }
158
159         exit (error);
160 }
161
162 static void
163 fatal(const char *format, ...)
164 {
165         va_list args;
166
167         va_start(args, format);
168         if (format)
169                 vfprintf(stderr, format, args);
170         fprintf(stderr, "\n");
171
172         exit(-1);
173 }
174
175 static void
176 getoptstr(char *str, size_t size, const char *argname)
177 {
178         if (strlen(optarg) > size)
179                 usage("'%s' too large", argname);
180         strlcpy(str, optarg, size);
181 }
182
183 static void
184 printxbe(ximgact_binmisc_entry_t *xbe)
185 {
186         uint32_t i, flags = xbe->xbe_flags;
187
188         if (xbe->xbe_version != IBE_VERSION) {
189                 fprintf(stderr, "Error: XBE version mismatch\n");
190                 return;
191         }
192
193         printf("name: %s\n", xbe->xbe_name);
194         printf("interpreter: %s\n", xbe->xbe_interpreter);
195         printf("flags: %s%s\n", (flags & IBF_ENABLED) ? "ENABLED " : "",
196             (flags & IBF_USE_MASK) ? "USE_MASK " : "");
197         printf("magic size: %u\n", xbe->xbe_msize);
198         printf("magic offset: %u\n", xbe->xbe_moffset);
199
200         printf("magic: ");
201         for(i = 0; i < xbe->xbe_msize;  i++) {
202                 if (i && !(i % 12))
203                         printf("\n       ");
204                 else
205                         if (i && !(i % 4))
206                                 printf(" ");
207                 printf("0x%02x ", xbe->xbe_magic[i]);
208         }
209         printf("\n");
210
211         if (flags & IBF_USE_MASK) {
212                 printf("mask:  ");
213                 for(i = 0; i < xbe->xbe_msize;  i++) {
214                         if (i && !(i % 12))
215                                 printf("\n       ");
216                         else
217                                 if (i && !(i % 4))
218                                         printf(" ");
219                         printf("0x%02x ", xbe->xbe_mask[i]);
220                 }
221                 printf("\n");
222         }
223
224         printf("\n");
225 }
226
227 static int
228 demux_cmd(__unused int argc, char *const argv[])
229 {
230         size_t i;
231
232         optind = 1;
233         optreset = 1;
234
235         for(i = 0; i < ( sizeof (cmds) / sizeof (cmds[0])); i++) {
236                 if (!strcasecmp(cmds[i].name, argv[0])) {
237                         return (i);
238                 }
239         }
240
241         /* Unknown command */
242         return (-1);
243 }
244
245 static int
246 strlit2bin_cpy(uint8_t *d, char *s, size_t size)
247 {
248         int c;
249         size_t cnt = 0;
250
251         while((c = *s++) != '\0') {
252                 if (c == '\\') {
253                         /* Do '\' escapes. */
254                         switch (*s) {
255                         case '\\':
256                                 *d++ = '\\';
257                                 break;
258
259                         case 'x':
260                                 s++;
261                                 c = toupper(*s++);
262                                 *d = (c - (isdigit(c) ? '0' : ('A' - 10))) << 4;
263                                 c = toupper(*s++);
264                                 *d++ |= c - (isdigit(c) ? '0' : ('A' - 10));
265                                 break;
266
267                         default:
268                                 return (-1);
269                         }
270                 } else
271                         *d++ = c;
272
273                 if (++cnt > size)
274                         return (-1);
275         }
276
277         return (cnt);
278 }
279
280 int
281 add_cmd(__unused int argc, char *argv[], ximgact_binmisc_entry_t *xbe)
282 {
283         int ch;
284         char *magic = NULL, *mask = NULL;
285         int sz;
286
287         if (strlen(argv[0]) > IBE_NAME_MAX)
288                 usage("'%s' string length longer than IBE_NAME_MAX (%d)",
289                     IBE_NAME_MAX);
290         strlcpy(&xbe->xbe_name[0], argv[0], IBE_NAME_MAX);
291
292         while ((ch = getopt_long(argc, argv, "ei:m:M:o:s:", add_opts, NULL))
293             != -1) {
294
295                 switch(ch) {
296                 case 'i':
297                         getoptstr(xbe->xbe_interpreter, IBE_INTERP_LEN_MAX,
298                             "interpreter");
299                         break;
300
301                 case 'm':
302                         free(magic);
303                         magic = strdup(optarg);
304                         break;
305
306                 case 'M':
307                         free(mask);
308                         mask = strdup(optarg);
309                         xbe->xbe_flags |= IBF_USE_MASK;
310                         break;
311
312                 case 'e':
313                         xbe->xbe_flags |= IBF_ENABLED;
314                         break;
315
316                 case 'o':
317                         xbe->xbe_moffset = atol(optarg);
318                         break;
319
320                 case 's':
321                         xbe->xbe_msize = atol(optarg);
322                         if (xbe->xbe_msize == 0 ||
323                             xbe->xbe_msize > IBE_MAGIC_MAX)
324                                 usage("Error: Not valid '--size' value. "
325                                     "(Must be > 0 and < %u.)\n",
326                                     xbe->xbe_msize);
327                         break;
328
329                 default:
330                         usage("Unknown argument: '%c'", ch);
331                 }
332         }
333
334         if (xbe->xbe_msize == 0) {
335                 if (NULL != magic)
336                         free(magic);
337                 if (NULL != mask)
338                         free(mask);
339                 usage("Error: Missing '--size' argument");
340         }
341
342         if (NULL != magic) {
343                 if (xbe->xbe_msize == 0) {
344                         if (magic)
345                                 free(magic);
346                         if (mask)
347                                 free(mask);
348                         usage("Error: Missing magic size argument");
349                 }
350                 sz = strlit2bin_cpy(xbe->xbe_magic, magic, IBE_MAGIC_MAX);
351                 free(magic);
352                 if (sz == -1 || (uint32_t)sz != xbe->xbe_msize) {
353                         if (mask)
354                                 free(mask);
355                         usage("Error: invalid magic argument");
356                 }
357                 if (mask) {
358                         sz = strlit2bin_cpy(xbe->xbe_mask, mask, IBE_MAGIC_MAX);
359                         free(mask);
360                         if (sz == -1 || (uint32_t)sz != xbe->xbe_msize)
361                                 usage("Error: invalid mask argument");
362                 }
363         } else {
364                 if (mask)
365                         free(mask);
366                 usage("Error: Missing magic argument");
367         }
368
369         if (!strnlen(xbe->xbe_interpreter, IBE_INTERP_LEN_MAX)) {
370                 usage("Error: Missing 'interpreter' argument");
371         }
372
373         return (0);
374 }
375
376 int
377 name_cmd(int argc, char *argv[], ximgact_binmisc_entry_t *xbe)
378 {
379         if (argc == 0)
380                 usage("Required argument missing\n");
381         if (strlen(argv[0]) > IBE_NAME_MAX)
382                 usage("'%s' string length longer than IBE_NAME_MAX (%d)",
383                     IBE_NAME_MAX);
384         strlcpy(&xbe->xbe_name[0], argv[0], IBE_NAME_MAX);
385
386         return (0);
387 }
388
389 int
390 noname_cmd(__unused int argc, __unused char *argv[],
391     __unused ximgact_binmisc_entry_t *xbe)
392 {
393
394         return (0);
395 }
396
397 int
398 main(int argc, char **argv)
399 {
400         int error = 0, cmd = -1;
401         ximgact_binmisc_entry_t xbe_in, *xbe_inp = NULL;
402         ximgact_binmisc_entry_t xbe_out, *xbe_outp = NULL;
403         size_t xbe_in_sz = 0;
404         size_t xbe_out_sz = 0, *xbe_out_szp = NULL;
405         uint32_t i;
406
407         if (modfind(KMOD_NAME) == -1) {
408                 if (kldload(KMOD_NAME) == -1)
409                         fatal("Can't load %s kernel module: %s",
410                             KMOD_NAME, strerror(errno));
411         }
412
413         bzero(&xbe_in, sizeof(xbe_in));
414         bzero(&xbe_out, sizeof(xbe_out));
415         xbe_in.xbe_version = IBE_VERSION;
416
417         if (argc < 2)
418                 usage("Error: requires at least one argument");
419
420         argc--, argv++;
421         cmd = demux_cmd(argc, argv);
422         if (cmd < 0)
423                 usage("Error: Unknown command \"%s\"", argv[0]);
424         argc--, argv++;
425
426         error = (*cmds[cmd].func)(argc, argv, &xbe_in);
427         if (error)
428                 usage("Can't parse command-line for '%s' command",
429                     cmds[cmd].name);
430
431         if (cmd != CMD_LIST) {
432                 xbe_inp = &xbe_in;
433                 xbe_in_sz = sizeof(xbe_in);
434         } else
435                 xbe_out_szp = &xbe_out_sz;
436         if (cmd == CMD_LOOKUP) {
437                 xbe_out_sz = sizeof(xbe_out);
438                 xbe_outp = &xbe_out;
439                 xbe_out_szp = &xbe_out_sz;
440         }
441
442         error = sysctlbyname(cmd_sysctl_name[cmd], xbe_outp, xbe_out_szp,
443             xbe_inp, xbe_in_sz);
444
445         if (error)
446                 switch(errno) {
447                 case EINVAL:
448                         usage("Invalid interpreter name or --interpreter, "
449                             "--magic, --mask, or --size argument value");
450                         break;
451
452                 case EEXIST:
453                         usage("'%s' is not unique in activator list",
454                             xbe_in.xbe_name);
455                         break;
456
457                 case ENOENT:
458                         usage("'%s' is not found in activator list",
459                             xbe_in.xbe_name);
460                         break;
461
462                 case ENOSPC:
463                         fatal("Fatal: no more room in the activator list "
464                             "(limited to %d enties)", IBE_MAX_ENTRIES);
465                         break;
466
467                 case EPERM:
468                         usage("Insufficient privileges for '%s' command",
469                             cmds[cmd].name);
470                         break;
471
472                 default:
473                         fatal("Fatal: sysctlbyname() returned: %s",
474                             strerror(errno));
475                         break;
476                 }
477
478
479         if (cmd == CMD_LOOKUP)
480                 printxbe(xbe_outp);
481
482         if (cmd == CMD_LIST && xbe_out_sz > 0) {
483                 xbe_outp = malloc(xbe_out_sz);
484                 if (!xbe_outp)
485                         fatal("Fatal: out of memory");
486                 while(1) {
487                         size_t osize = xbe_out_sz;
488                         error = sysctlbyname(cmd_sysctl_name[cmd], xbe_outp,
489                             &xbe_out_sz, NULL, 0);
490
491                         if (error == -1 && errno == ENOMEM &&
492                             xbe_out_sz == osize) {
493                                 /*
494                                  * Buffer too small. Increase it by one
495                                  * entry.
496                                  */
497                                 xbe_out_sz += sizeof(xbe_out);
498                                 xbe_outp = realloc(xbe_outp, xbe_out_sz);
499                                 if (!xbe_outp)
500                                         fatal("Fatal: out of memory");
501                         } else
502                                 break;
503                 }
504                 if (error) {
505                         free(xbe_outp);
506                         fatal("Fatal: %s", strerror(errno));
507                 }
508                 for(i = 0; i < (xbe_out_sz / sizeof(xbe_out)); i++)
509                         printxbe(&xbe_outp[i]);
510         }
511
512         return (error);
513 }