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