]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - stand/common/install.c
libarchive: merge security fix from vendor branch
[FreeBSD/FreeBSD.git] / stand / common / install.c
1 /*-
2  * Copyright (c) 2008-2014, Juniper Networks, Inc.
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 ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
19  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
21  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22  * 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 #include <sys/cdefs.h>
28 #include <sys/param.h>
29 #include <sys/socket.h>
30 #include <net/if.h>
31 #include <netinet/in.h>
32 #include <netinet/in_systm.h>
33
34 #include <stand.h>
35 #include <net.h>
36 #include <string.h>
37
38 #include "bootstrap.h"
39
40 extern struct in_addr servip;
41
42 extern int pkgfs_init(const char *, struct fs_ops *);
43 extern void pkgfs_cleanup(void);
44
45 COMMAND_SET(install, "install", "install software package", command_install);
46
47 static char *inst_kernel;
48 static char **inst_modules;
49 static char *inst_rootfs;
50 static char *inst_loader_rc;
51
52 static int
53 setpath(char **what, char *val)
54 {
55         char *path;
56         size_t len;
57         int rel;
58
59         len = strlen(val) + 1;
60         rel = (val[0] != '/') ? 1 : 0;
61         path = malloc(len + rel);
62         if (path == NULL)
63                 return (ENOMEM);
64         path[0] = '/';
65         strcpy(path + rel, val);
66
67         *what = path;
68         return (0);
69 }
70
71 static int
72 setmultipath(char ***what, char *val)
73 {
74         char *s, *v;
75         int count, error, idx;
76
77         count = 0;
78         v = val;
79         do {
80                 count++;
81                 s = strchr(v, ',');
82                 v = (s == NULL) ? NULL : s + 1;
83         } while (v != NULL);
84
85         *what = calloc(count + 1, sizeof(char *));
86         if (*what == NULL)
87                 return (ENOMEM);
88
89         for (idx = 0; idx < count; idx++) {
90                 s = strchr(val, ',');
91                 if (s != NULL)
92                         *s++ = '\0';
93                 error = setpath(*what + idx, val);
94                 if (error)
95                         return (error);
96                 val = s;
97         }
98
99         return (0);
100 }
101
102 static int
103 read_metatags(int fd)
104 {
105         char buf[1024];
106         char *p, *tag, *val;
107         ssize_t fsize;
108         int error;
109
110         fsize = read(fd, buf, sizeof(buf));
111         if (fsize == -1)
112                 return (errno);
113
114         /*
115          * Assume that if we read a whole buffer worth of data, we
116          * haven't read the entire file. In other words, the buffer
117          * size must always be larger than the file size. That way
118          * we can append a '\0' and use standard string operations.
119          * Return an error if this is not possible.
120          */
121         if (fsize == sizeof(buf))
122                 return (ENOMEM);
123
124         buf[fsize] = '\0';
125         error = 0;
126         tag = buf;
127         while (!error && *tag != '\0') {
128                 val = strchr(tag, '=');
129                 if (val == NULL) {
130                         error = EINVAL;
131                         break;
132                 }
133                 *val++ = '\0';
134                 p = strchr(val, '\n');
135                 if (p == NULL) {
136                         error = EINVAL;
137                         break;
138                 }
139                 *p++ = '\0';
140
141                 if (strcmp(tag, "KERNEL") == 0)
142                         error = setpath(&inst_kernel, val);
143                 else if (strcmp(tag, "MODULES") == 0)
144                         error = setmultipath(&inst_modules, val);
145                 else if (strcmp(tag, "ROOTFS") == 0)
146                         error = setpath(&inst_rootfs, val);
147                 else if (strcmp(tag, "LOADER_RC") == 0)
148                         error = setpath(&inst_loader_rc, val);
149
150                 tag = p;
151         }
152
153         return (error);
154 }
155
156 static void
157 cleanup(void)
158 {
159         u_int i;
160
161         if (inst_kernel != NULL) {
162                 free(inst_kernel);
163                 inst_kernel = NULL;
164         }
165         if (inst_modules != NULL) {
166                 i = 0;
167                 while (inst_modules[i] != NULL)
168                         free(inst_modules[i++]);
169                 free(inst_modules);
170                 inst_modules = NULL;
171         }
172         if (inst_rootfs != NULL) {
173                 free(inst_rootfs);
174                 inst_rootfs = NULL;
175         }
176         if (inst_loader_rc != NULL) {
177                 free(inst_loader_rc);
178                 inst_loader_rc = NULL;
179         }
180         pkgfs_cleanup();
181 }
182
183 /*
184  * usage: install URL
185  * where: URL = tftp://[host]/<package>
186  *      or      file://[devname[:fstype]]/<package>
187  */
188 static int
189 install(char *pkgname)
190 {
191         static char buf[256];
192         struct fs_ops *proto;
193         struct preloaded_file *fp;
194         char *e, *s, *currdev;
195         char *devname;
196         size_t devnamelen;
197         int error, fd, i, local;
198
199         s = strstr(pkgname, "://");
200         if (s == NULL)
201                 goto invalid_url;
202
203         i = s - pkgname;
204         s += 3;
205         if (*s == '\0')
206                 goto invalid_url;
207
208         devname = NULL;
209         devnamelen = 0;
210         proto = NULL;
211         local = 0;
212
213         if (i == 4 && !strncasecmp(pkgname, "tftp", i)) {
214                 devname = "net0";
215                 devnamelen = 4;
216                 proto = &tftp_fsops;
217         } else if (i == 4 && !strncasecmp(pkgname, "file", i)) {
218                 currdev = getenv("currdev");
219                 local = 1;
220
221                 if (*s == '/') {        /* file:/// */
222                         if (devname == NULL)
223                                 devname = currdev;
224                         if (devname == NULL)
225                                 devname = "disk1";
226                 } else {                /* file://devname[:fstype]/ */
227                         devname = s;
228                         e = strchr(devname, '/');
229                         if (!e)
230                                 goto invalid_url;
231                         devnamelen = e - devname;
232                         s = e;          /* consume devname */
233                 }
234                 if ((e = strchr(devname, ':')) != NULL) {
235                         /* could be :fstype */
236                         devnamelen = e - devname;
237                         switch (e[1]) {
238                         case '\0':      /* just currdev */
239                                 break;
240                         case 'd':
241                                 proto = &dosfs_fsops;
242                                 break;
243 #ifdef HOSTPROG
244                         case 'h':
245                                 {
246                                         extern struct fs_ops host_fsops;
247
248                                         proto = &host_fsops;
249                                 }
250                                 break;
251 #endif
252                         case 'u':
253                                 proto = &ufs_fsops;
254                                 break;
255                         }
256                 }
257                 if (proto == NULL && strncmp(devname, "disk", 4) == 0) {
258                         proto = &dosfs_fsops;
259                 }
260         }
261
262         if (devname == NULL)
263                 goto invalid_url;
264
265         if (devnamelen == 0) {
266                 /* default is currdev which ends with ':' */
267                 devnamelen = strlen(devname);
268                 if (devname[devnamelen - 1] == ':')
269                         devnamelen--;
270         }
271
272         if (*s != '/' ) {
273                 if (local)
274                         goto invalid_url;
275
276                 pkgname = strchr(s, '/');
277                 if (pkgname == NULL)
278                         goto invalid_url;
279
280                 *pkgname = '\0';
281                 servip.s_addr = inet_addr(s);
282                 if (servip.s_addr == htonl(INADDR_NONE))
283                         goto invalid_url;
284
285                 setenv("serverip", inet_ntoa(servip), 1);
286
287                 *pkgname = '/';
288         } else
289                 pkgname = s;
290
291         i = snprintf(buf, sizeof(buf), "%.*s:%s",
292             (int) devnamelen, devname, pkgname);
293         if (i >= (int) sizeof(buf)) {
294                 command_errmsg = "package name too long";
295                 return (CMD_ERROR);
296         }
297         setenv("install_package", buf, 1);
298
299         error = pkgfs_init(buf, proto);
300         if (error) {
301                 command_errmsg = "cannot open package";
302                 goto fail;
303         }
304
305         /*
306          * Point of no return: unload anything that may have been
307          * loaded and prune the environment from harmful variables.
308          */
309         unload();
310         unsetenv("vfs.root.mountfrom");
311
312         /*
313          * read the metatags file.
314          */
315         fd = open("/metatags", O_RDONLY);
316         if (fd != -1) {
317                 error = read_metatags(fd);
318                 close(fd);
319                 if (error) {
320                         command_errmsg = "cannot load metatags";
321                         goto fail;
322                 }
323         }
324
325         s = (inst_kernel == NULL) ? "/kernel" : inst_kernel;
326         error = mod_loadkld(s, 0, NULL);
327         if (error) {
328                 command_errmsg = "cannot load kernel from package";
329                 goto fail;
330         }
331
332         /* If there is a loader.rc in the package, execute it */
333         s = (inst_loader_rc == NULL) ? "/loader.rc" : inst_loader_rc;
334         fd = open(s, O_RDONLY);
335         if (fd != -1) {
336                 close(fd);
337                 error = interp_include(s);
338                 if (error == CMD_ERROR)
339                         goto fail;
340         }
341
342         i = 0;
343         while (inst_modules != NULL && inst_modules[i] != NULL) {
344                 error = mod_loadkld(inst_modules[i], 0, NULL);
345                 if (error) {
346                         command_errmsg = "cannot load module(s) from package";
347                         goto fail;
348                 }
349                 i++;
350         }
351
352         s = (inst_rootfs == NULL) ? "/install.iso" : inst_rootfs;
353         if (file_loadraw(s, "mfs_root", 1) == NULL) {
354                 error = errno;
355                 command_errmsg = "cannot load root file system";
356                 goto fail;
357         }
358
359         cleanup();
360
361         fp = file_findfile(NULL, NULL);
362         if (fp != NULL)
363                 file_formats[fp->f_loader]->l_exec(fp);
364         error = CMD_ERROR;
365         command_errmsg = "unable to start installation";
366
367  fail:
368         sprintf(buf, "%s (error %d)", command_errmsg, error);
369         cleanup();
370         unload();
371         exclusive_file_system = NULL;
372         command_errmsg = buf;   /* buf is static. */
373         return (CMD_ERROR);
374
375  invalid_url:
376         command_errmsg = "invalid URL";
377         return (CMD_ERROR);
378 }
379
380 static int
381 command_install(int argc, char *argv[])
382 {
383         int argidx;
384
385         unsetenv("install_format");
386
387         argidx = 1;
388         while (1) {
389                 if (argc == argidx) {
390                         command_errmsg =
391                             "usage: install [--format] <URL>";
392                         return (CMD_ERROR);
393                 }
394                 if (!strcmp(argv[argidx], "--format")) {
395                         setenv("install_format", "yes", 1);
396                         argidx++;
397                         continue;
398                 }
399                 break;
400         }
401
402         return (install(argv[argidx]));
403 }