Mercurial > trustbridge
comparison cinst/certificate-installer.c @ 1176:c8f698ca6355
(issue128) Rename cinst to trustbridge-certificate-installer
author | Andre Heinecke <andre.heinecke@intevation.de> |
---|---|
date | Mon, 22 Sep 2014 11:34:06 +0200 |
parents | cinst/main.c@2a1206932f53 |
children | 12ed0b72e9f5 |
comparison
equal
deleted
inserted
replaced
1175:e210ecc32d69 | 1176:c8f698ca6355 |
---|---|
1 /* Copyright (C) 2014 by Bundesamt für Sicherheit in der Informationstechnik | |
2 * Software engineering by Intevation GmbH | |
3 * | |
4 * This file is Free Software under the GNU GPL (v>=2) | |
5 * and comes with ABSOLUTELY NO WARRANTY! | |
6 * See LICENSE.txt for details. | |
7 */ | |
8 /** | |
9 * @file certificate-installer.c | |
10 * @brief Main entry point for the certificate install process. | |
11 * | |
12 * The certificate installer process may or may not be run with elevated | |
13 * privileges. When run with elevated privileges this | |
14 * process will modify system wide certificate stores. | |
15 * Otherwise only the users certificate stores are modified. | |
16 * | |
17 * The first parameter to this process should be list=\<file_name\> | |
18 * of the certificate list to work on. The second parameter should | |
19 * be choices=\<choices_file_name\>|uninstall | |
20 * | |
21 * choices_file_name should be the absolute path to an | |
22 * choices file formatted as: | |
23 * | |
24 * I:\<certificate\><BR> | |
25 * R:\<certificate\> | |
26 * | |
27 * Line breaks can be system dependent in the Choices file. | |
28 * | |
29 * It will only execute the choices if the | |
30 * I and R choices are also part of the signed | |
31 * certificate list. The signature is validated with the | |
32 * built in key. | |
33 * | |
34 * The special instruction "uninstall" will cause the installer | |
35 * to remove all certificates (Even those marked with I) that | |
36 * are part of the list. | |
37 * | |
38 * For more verbose debug output add --debug to the call. | |
39 * | |
40 **/ | |
41 #include <stdio.h> | |
42 #include <stdlib.h> | |
43 #include <string.h> | |
44 #include <assert.h> | |
45 #include <stdbool.h> | |
46 | |
47 #include "strhelp.h" | |
48 #include "listutil.h" | |
49 #include "logging.h" | |
50 #include "errorcodes.h" | |
51 #include "windowsstore.h" | |
52 #include "nssstore.h" | |
53 #include "portpath.h" | |
54 | |
55 /* The certificate list + choices may only be so long as | |
56 * twice the accepted certificatelist size */ | |
57 #define MAX_INPUT_SIZE MAX_LINE_LENGTH * MAX_LINES * 2 | |
58 | |
59 /* @brief Read stdin into data structures. | |
60 * | |
61 * Reads choices from an input file into the to_install | |
62 * and to_remove buffers. | |
63 * | |
64 * Lines starting with I: are treated as install choices. | |
65 * Lines starting with R: are treated as remove choices. | |
66 * Other lines are ignored. | |
67 * | |
68 * Terminates in OOM conditions. | |
69 * | |
70 * The caller needs to free the memory allocated by this function | |
71 * even when an error is returned. | |
72 * | |
73 * @param[in] file_name absolute path to the choices file. | |
74 * @param[out] to_install strv of installation choices or NULL | |
75 * @param[out] to_remove strv of remove choices or NULL | |
76 * | |
77 * @returns: 0 on success. An error code otherwise. | |
78 */ | |
79 static int | |
80 read_choices_file (char *file_name, char ***to_install, | |
81 char ***to_remove) | |
82 { | |
83 int lines_read = 0; | |
84 char buf[MAX_LINE_LENGTH + 2]; | |
85 FILE *f = NULL; | |
86 long file_size; | |
87 | |
88 if (*to_install || *to_remove) | |
89 { | |
90 ERRORPRINTF ("Error invalid parameters.\n"); | |
91 return -1; | |
92 } | |
93 | |
94 f = port_fopen_rb(file_name, false); | |
95 if (f == NULL) | |
96 { | |
97 ERRORPRINTF ("Failed to open file: %s\n", file_name); | |
98 return ERR_NO_INSTRUCTIONS; | |
99 } | |
100 | |
101 fseek (f, 0, SEEK_END); | |
102 file_size = ftell (f); | |
103 if (file_size <= 0) | |
104 { | |
105 fclose (f); | |
106 ERRORPRINTF ("File size error: %s\n", file_name); | |
107 return ERR_NO_INSTRUCTIONS; | |
108 } | |
109 | |
110 fseek (f, 0, SEEK_SET); | |
111 | |
112 if (file_size + 1 == 0) | |
113 { | |
114 fclose (f); | |
115 ERRORPRINTF ("File seek error: %s\n", file_name); | |
116 return ERR_INVALID_INSTRUCTIONS; | |
117 } | |
118 | |
119 while (fgets (buf, MAX_LINE_LENGTH + 1, f) ) | |
120 { | |
121 size_t len = strlen (buf); /* fgets ensures buf is terminated */ | |
122 if (len <= 3) | |
123 { | |
124 ERRORPRINTF ("Line too short.\n"); | |
125 fclose (f); | |
126 return ERR_INVALID_INPUT; | |
127 } | |
128 if (lines_read++ > MAX_LINES) | |
129 { | |
130 ERRORPRINTF ("Too many lines\n"); | |
131 fclose (f); | |
132 return ERR_TOO_MUCH_INPUT; | |
133 } | |
134 if (*buf == 'I') | |
135 { | |
136 char *trimmed = buf+2; | |
137 /* Remove leading I: and trailing whitespace */ | |
138 str_trim(&trimmed); | |
139 strv_append (to_install, trimmed, strlen(trimmed)); | |
140 continue; | |
141 } | |
142 if (*buf == 'R') | |
143 { | |
144 char *trimmed = buf+2; | |
145 /* Remove leading R: and trailing whitespace */ | |
146 str_trim(&trimmed); | |
147 strv_append (to_remove, trimmed, strlen(trimmed)); | |
148 continue; | |
149 } | |
150 } | |
151 | |
152 fclose (f); | |
153 return 0; | |
154 } | |
155 | |
156 /** @brief Check that the insturctions match to the list | |
157 * | |
158 * Only certificates part of the certificate_list are allowed | |
159 * for installation. | |
160 * | |
161 * @param[in] all_certs strv of all valid certificates in a list | |
162 * @param[in] to_validate strv of choices | |
163 * | |
164 * @returns 0 on success, an error otherwise | |
165 */ | |
166 int | |
167 validate_choices (char **all_certs, char **to_validate) | |
168 { | |
169 int i = 0, j = 0; | |
170 | |
171 if (!all_certs || strv_length (all_certs) < 1) | |
172 { | |
173 /* Invalid parameters */ | |
174 return -1; | |
175 } | |
176 | |
177 if (to_validate == NULL) | |
178 { | |
179 /* Nothing is valid */ | |
180 return 0; | |
181 } | |
182 | |
183 for (i = 0; to_validate[i]; i++) | |
184 { | |
185 bool found = false; | |
186 for (j = 0; all_certs[j]; j++) | |
187 { | |
188 if (strncmp (to_validate[i], all_certs[j], MAX_LINE_LENGTH) == | |
189 0) | |
190 { | |
191 found = true; | |
192 break; | |
193 } | |
194 } | |
195 if (!found) | |
196 { | |
197 DEBUGPRINTF ("Failed to find certificate; \n%s\n", to_validate[i]); | |
198 return ERR_INVALID_INSTRUCTIONS; | |
199 } | |
200 } | |
201 | |
202 return 0; | |
203 } | |
204 | |
205 #ifdef DO_RELEASE_BUILD | |
206 bool g_debug = false; | |
207 #else | |
208 bool g_debug = true; | |
209 #endif | |
210 | |
211 int | |
212 main (int argc, char **argv) | |
213 { | |
214 char **to_install = NULL, | |
215 **to_remove = NULL, | |
216 **all_valid_certs = NULL; | |
217 int ret = -1; | |
218 | |
219 char *certificate_list = NULL, | |
220 *certificate_file_name = NULL, | |
221 *choices_file_name = NULL; | |
222 size_t list_len = 0; | |
223 list_status_t list_status; | |
224 bool do_uninstall = false; | |
225 | |
226 /* Some very static argument parsing. list= and choices= is only | |
227 added to make it more transparent how this programm is called if | |
228 a user looks at the detailed uac dialog. */ | |
229 if ((argc != 3 && argc != 4) || strncmp(argv[1], "list=", 5) != 0 || | |
230 strncmp(argv[2], "choices=", 8) != 0) | |
231 { | |
232 ERRORPRINTF ("Invalid arguments.\n" | |
233 "Expected arguments: list=<certificate_list> \n" | |
234 " choices=<choices_file>|uninstall\n" | |
235 "Optional: --debug\n"); | |
236 return ERR_INVALID_PARAMS; | |
237 } | |
238 | |
239 certificate_file_name = strchr(argv[1], '=') + 1; | |
240 choices_file_name = strchr(argv[2], '=') + 1; | |
241 | |
242 if (argc == 4 && strncmp(argv[3], "--debug", 7) == 0) | |
243 { | |
244 g_debug = true; | |
245 } | |
246 | |
247 if (!certificate_file_name || !choices_file_name) | |
248 { | |
249 ERRORPRINTF ("Invalid arguments.\n" | |
250 "Expected arguments: list=<certificate_list> \n" | |
251 " choices=<choices_file>|uninstall\n"); | |
252 return ERR_INVALID_PARAMS; | |
253 } | |
254 | |
255 if (strncmp(choices_file_name, "uninstall", 9) == 0) | |
256 { | |
257 do_uninstall = true; | |
258 choices_file_name = NULL; | |
259 } | |
260 | |
261 list_status = read_and_verify_list (certificate_file_name, &certificate_list, | |
262 &list_len); | |
263 | |
264 if (list_status != Valid) | |
265 { | |
266 if (list_status == InvalidSignature) | |
267 { | |
268 ERRORPRINTF ("Failed to verify signature.\n"); | |
269 return ERR_INVALID_SIGNATURE; | |
270 } | |
271 | |
272 ERRORPRINTF ("Failed to read certificate list.\n"); | |
273 return ERR_INVALID_INPUT_NO_LIST; | |
274 } | |
275 | |
276 all_valid_certs = get_certs_from_list (certificate_list, list_len); | |
277 free (certificate_list); | |
278 | |
279 if (!all_valid_certs) | |
280 { | |
281 /* Impossible */ | |
282 return -1; | |
283 } | |
284 | |
285 | |
286 /* For uninstall we are done now */ | |
287 if (do_uninstall) | |
288 { | |
289 #ifdef WIN32 | |
290 ret = write_stores_win (NULL, all_valid_certs); | |
291 if (ret != 0) | |
292 { | |
293 ERRORPRINTF ("Failed to write windows stores retval: %i\n", ret); | |
294 } | |
295 #endif | |
296 ret = write_stores_nss (NULL, all_valid_certs); | |
297 strv_free (all_valid_certs); | |
298 return ret; | |
299 } | |
300 | |
301 ret = read_choices_file (choices_file_name, &to_install, | |
302 &to_remove); | |
303 | |
304 if (ret) | |
305 { | |
306 ERRORPRINTF ("Failed to read choices file\n"); | |
307 return ret; | |
308 } | |
309 | |
310 if (!strv_length (to_install) && !strv_length (to_remove) ) | |
311 { | |
312 ERRORPRINTF ("Failed to read choices file\n"); | |
313 return ERR_NO_INSTRUCTIONS; | |
314 } | |
315 | |
316 /* Check that the choices are ok to execute */ | |
317 if (to_install) | |
318 { | |
319 ret = validate_choices (all_valid_certs, to_install); | |
320 if (ret) | |
321 { | |
322 ERRORPRINTF ("Failed to validate choices\n"); | |
323 return ret; | |
324 } | |
325 } | |
326 | |
327 if (to_remove) | |
328 { | |
329 ret = validate_choices (all_valid_certs, to_remove); | |
330 if (ret) | |
331 { | |
332 ERRORPRINTF ("Failed to validate removal choices\n"); | |
333 return ret; | |
334 } | |
335 } | |
336 | |
337 strv_free (all_valid_certs); | |
338 | |
339 #ifdef WIN32 | |
340 ret = write_stores_win (to_install, to_remove); | |
341 if (ret != 0) | |
342 { | |
343 ERRORPRINTF ("Failed to write windows stores retval: %i\n", ret); | |
344 } | |
345 #endif | |
346 ret = write_stores_nss (to_install, to_remove); | |
347 if (ret != 0) | |
348 { | |
349 ERRORPRINTF ("Failed to write nss stores"); | |
350 } | |
351 | |
352 /* Make valgrind happy */ | |
353 strv_free (to_install); | |
354 strv_free (to_remove); | |
355 | |
356 return 0; | |
357 } |