]> git.ipfire.org Git - people/pmueller/ipfire-2.x.git/blob - src/misc-progs/installpackage.c
dab9864d934a2cf38368938665a282a561ac5fb4
[people/pmueller/ipfire-2.x.git] / src / misc-progs / installpackage.c
1 /* This file is part of the IPCop Firewall.
2 *
3 * This program is distributed under the terms of the GNU General Public
4 * Licence. See the file COPYING for details.
5 *
6 * Copyright (C) 2004-05-31 Robert Kerr <rkerr@go.to>
7 *
8 * Loosely based on the smoothwall helper program by the same name,
9 * portions are (c) Lawrence Manning, 2001
10 *
11 * $Id: installpackage.c,v 1.3.2.6 2005/08/22 20:51:38 eoberlander Exp $
12 *
13 */
14
15 #include <stdio.h>
16 #include <string.h>
17 #include <stdlib.h>
18 #include <unistd.h>
19 #include <errno.h>
20 #include <sys/file.h>
21 #include <fcntl.h>
22 #include <syslog.h>
23 #include <time.h>
24 #include "setuid.h"
25
26 #define ERR_ANY 1
27 #define ERR_TMPDIR 2
28 #define ERR_SIG 3
29 #define ERR_TAR 4
30 #define ERR_INFO 5
31 #define ERR_PACKLIST 6
32 #define ERR_INSTALLED 7
33 #define ERR_POPEN 8
34 #define ERR_SETUP 9
35 #define ERR_MISSING_PREVIOUS 10
36 #define ERR_DISK 11
37
38 /* The lines in the package information file and the patches/installed list
39 * are often longer than STRING_SIZE so we use a larger buffer */
40 #define BUFFER_SIZE 4096
41
42 char *info = NULL;
43 FILE *infofile = NULL;
44 char command[STRING_SIZE], tmpdir[] = "/var/log/pat_install_XXXXXX";
45 void exithandler(void)
46 {
47 if(info) free(info);
48 if(infofile)
49 {
50 flock(fileno(infofile), LOCK_UN);
51 fclose(infofile);
52 }
53 /* Cleanup tmpdir */
54 chdir("/var/patches"); /* get out of it before erasing */
55 snprintf(command, STRING_SIZE - 1, "/bin/rm -rf %s", tmpdir);
56 if(safe_system(command))
57 perror("Couldn't remove temp dir");
58 }
59
60 int main(int argc, char *argv[])
61 {
62 char buffer[BUFFER_SIZE];
63 int ret;
64 FILE *p;
65
66 if (!(initsetuid()))
67 exit(1);
68
69 /* Sanitize arguments */
70 if (argc < 2)
71 {
72 fprintf(stderr, "Missing arg\n");
73 exit(1);
74 }
75 if (strspn(argv[1], NUMBERS) != strlen(argv[1]))
76 {
77 fprintf(stderr, "Bad arg\n");
78 exit(1);
79 }
80
81 if(!mkdtemp(tmpdir))
82 {
83 perror("Unable to create secure temp dir");
84 exit(ERR_TMPDIR);
85 }
86
87 /* now exithandler will have something to erase */
88 atexit(exithandler);
89
90 /* verify and extract package */
91 memset(command, 0, STRING_SIZE);
92 snprintf(command, STRING_SIZE-1, "/usr/bin/gpg --batch --homedir /root/.gnupg -o %s/patch.tar.gz --decrypt /var/patches/patch-%s.tar.gz.gpg", tmpdir, argv[1]);
93 ret = safe_system(command) >> 8;
94 if(ret==1) /* 1=> gpg-key error */
95 {
96 fprintf(stderr, "Invalid package: signature check failed\n");
97 exit(ERR_SIG);
98 }
99 if(ret==2) /* 2=> gpg pub key not found */
100 {
101 fprintf(stderr, "Public signature not found (who signed package?) !\n");
102 exit(ERR_SIG);
103 }
104 if(ret) /* retry extraction on other partition */
105 {
106 rmdir(tmpdir);
107 strcpy (tmpdir,"/var/patches/install_XXXXXX");
108 if(!mkdtemp(tmpdir))
109 {
110 perror("Unable to create secure temp dir");
111 _exit(ERR_TMPDIR); /* no need exit handler */
112 }
113 memset(command, 0, STRING_SIZE);
114 snprintf(command, STRING_SIZE-1, "/usr/bin/gpg --batch --homedir /root/.gnupg -o %s/patch.tar.gz --decrypt /var/patches/patch-%s.tar.gz.gpg", tmpdir, argv[1]);
115 ret = safe_system(command);
116 if(ret)
117 {
118 fprintf(stderr, "Not enough disk space or gpg error %d !\n",ret);
119 exit(ERR_DISK);
120 }
121 }
122 /* no more needed gpg-package & make room */
123 snprintf(command, STRING_SIZE-1, "/var/patches/patch-%s.tar.gz.gpg", argv[1]);
124 unlink ( command );
125
126 /* unzip the package */
127 chdir (tmpdir);
128 if(safe_system("/bin/tar xzf patch.tar.gz"))
129 {
130 fprintf(stderr, "Invalid package: untar failed\n");
131 exit(ERR_TAR);
132 }
133 /* And read 'information' to check validity */
134 snprintf(buffer, STRING_SIZE-1, "%s/information", tmpdir);
135 if(!(infofile = fopen(buffer,"r")))
136 {
137 if(errno == ENOENT)
138 fprintf(stderr, "Invalid package: contains no information file\n");
139 else
140 perror("Unable to open package information file");
141 exit(ERR_INFO);
142 }
143 if(!fgets(buffer, BUFFER_SIZE, infofile))
144 {
145 perror("Couldn't read package information");
146 exit(ERR_INFO);
147 }
148 fclose(infofile);
149 if(buffer[strlen(buffer)-1] == '\n')
150 buffer[strlen(buffer)-1] = '\0';
151 if(!strchr(buffer,'|'))
152 {
153 fprintf(stderr, "Invalid package: malformed information string.\n");
154 exit(ERR_INFO);
155 }
156 info = strdup(buffer);
157
158 /* check if package is already installed */
159 if(!(infofile = fopen(CONFIG_ROOT "/patches/installed","r+")))
160 {
161 perror("Unable to open installed package list");
162 exit(ERR_PACKLIST);
163 }
164 /* get exclusive lock to prevent a mess if 2 copies run at once, and set
165 * close-on-exec flag so the FD doesn't leak to the setup script */
166 flock(fileno(infofile), LOCK_EX);
167 fcntl(fileno(infofile), F_SETFD, FD_CLOEXEC);
168
169 while(fgets(buffer, BUFFER_SIZE, infofile))
170 {
171 if(!strncmp(buffer, info, strlen(info)))
172 {
173 fprintf(stderr,"This package is already installed\n");
174 exit(ERR_INSTALLED);
175 }
176 }
177
178 /* install package */
179 openlog("installpackage", LOG_PID, LOG_USER);
180 snprintf(command, STRING_SIZE - 1, "%s/setup", tmpdir);
181 /* FIXME: popen suffers from the same environment problems as system() */
182 if (!(p = popen(command, "r")))
183 {
184 fprintf(stderr,"popen() failed\n");
185 closelog();
186 exit(ERR_POPEN);
187 }
188 setvbuf(p, NULL, _IOLBF, 255);
189 while (fgets(buffer, STRING_SIZE, p))
190 {
191 syslog(LOG_INFO, "%s", buffer);
192 }
193 ret = pclose(p);
194 closelog();
195
196 if(ret)
197 {
198 fprintf(stderr, "setup script returned exit code %d\n", ret>>8);
199 exit(ERR_SETUP);
200 }
201
202 /* write to package db */
203 if(strncmp(info, "000|", 4))
204 {
205 time_t curtime = time(NULL);
206 strftime(buffer, STRING_SIZE, "%Y-%m-%d", gmtime(&curtime));
207 fprintf(infofile, "%s|%s\n", info, buffer);
208 flock(fileno(infofile), LOCK_UN);
209 fclose(infofile);
210 } else { /* Full system upgrade to new version */
211 flock(fileno(infofile), LOCK_UN);
212 fclose(infofile);
213 unlink(CONFIG_ROOT "/patches/available");
214 unlink(CONFIG_ROOT "/patches/installed");
215 }
216 free(info);
217 exit(0);
218 }