]>
Commit | Line | Data |
---|---|---|
13e4a62f MW |
1 | /** |
2 | * @file local_credential_store.c | |
3 | * | |
4 | * @brief Implementation of local_credential_store_t. | |
5 | * | |
6 | */ | |
7 | ||
8 | /* | |
9 | * Copyright (C) 2006 Martin Willi | |
10 | * Hochschule fuer Technik Rapperswil | |
11 | * | |
12 | * This program is free software; you can redistribute it and/or modify it | |
13 | * under the terms of the GNU General Public License as published by the | |
14 | * Free Software Foundation; either version 2 of the License, or (at your | |
15 | * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. | |
16 | * | |
17 | * This program is distributed in the hope that it will be useful, but | |
18 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
19 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
20 | * for more details. | |
21 | */ | |
22 | ||
23 | #include <sys/stat.h> | |
24 | #include <dirent.h> | |
9820c0e2 | 25 | #include <string.h> |
13e4a62f MW |
26 | |
27 | #include "local_credential_store.h" | |
28 | ||
9820c0e2 | 29 | #include <utils/lexparser.h> |
13e4a62f MW |
30 | #include <utils/linked_list.h> |
31 | #include <utils/logger_manager.h> | |
32 | #include <crypto/x509.h> | |
33 | ||
9820c0e2 | 34 | #define PATH_BUF 256 |
13e4a62f MW |
35 | |
36 | typedef struct key_entry_t key_entry_t; | |
37 | ||
38 | /** | |
39 | * Private key with an associated ID to find it | |
40 | */ | |
41 | struct key_entry_t { | |
42 | ||
43 | /** | |
44 | * ID, as added | |
45 | */ | |
46 | identification_t *id; | |
47 | ||
48 | /** | |
49 | * Associated rsa private key | |
50 | */ | |
51 | rsa_private_key_t *key; | |
52 | }; | |
53 | ||
54 | ||
55 | typedef struct private_local_credential_store_t private_local_credential_store_t; | |
56 | ||
57 | /** | |
58 | * Private data of an local_credential_store_t object | |
59 | */ | |
60 | struct private_local_credential_store_t { | |
61 | ||
62 | /** | |
63 | * Public part | |
64 | */ | |
65 | local_credential_store_t public; | |
66 | ||
67 | /** | |
68 | * list of key_entry_t's with private keys | |
69 | */ | |
70 | linked_list_t *private_keys; | |
71 | ||
72 | /** | |
73 | * list of x509 certificates with public keys | |
74 | */ | |
75 | linked_list_t *certificates; | |
76 | ||
77 | /** | |
78 | * Assigned logger | |
79 | */ | |
80 | logger_t *logger; | |
81 | }; | |
82 | ||
83 | ||
84 | /** | |
85 | * Implementation of credential_store_t.get_shared_secret. | |
86 | */ | |
87 | static status_t get_shared_secret(private_local_credential_store_t *this, identification_t *identification, chunk_t *preshared_secret) | |
88 | { | |
89 | return FAILED; | |
90 | } | |
91 | ||
92 | /** | |
93 | * Implementation of credential_store_t.get_rsa_public_key. | |
94 | */ | |
95 | static rsa_public_key_t * get_rsa_public_key(private_local_credential_store_t *this, identification_t *identification) | |
96 | { | |
97 | x509_t *current; | |
98 | rsa_public_key_t *found = NULL; | |
99 | iterator_t *iterator; | |
100 | ||
101 | this->logger->log(this->logger, CONTROL|LEVEL2, "Looking for public key for %s", | |
102 | identification->get_string(identification)); | |
103 | iterator = this->certificates->create_iterator(this->certificates, TRUE); | |
104 | while (iterator->has_next(iterator)) | |
105 | { | |
106 | iterator->current(iterator, (void**)¤t); | |
107 | identification_t *stored = current->get_subject(current); | |
108 | this->logger->log(this->logger, CONTROL|LEVEL2, "there is one for %s", | |
109 | stored->get_string(stored)); | |
110 | if (identification->equals(identification, stored)) | |
111 | { | |
112 | found = current->get_public_key(current); | |
113 | break; | |
114 | } | |
115 | } | |
116 | iterator->destroy(iterator); | |
117 | return found; | |
118 | } | |
119 | ||
120 | /** | |
121 | * Implementation of credential_store_t.get_rsa_private_key. | |
122 | */ | |
123 | static rsa_private_key_t *get_rsa_private_key(private_local_credential_store_t *this, identification_t *identification) | |
124 | { | |
125 | rsa_private_key_t *found = NULL; | |
126 | key_entry_t *current; | |
127 | iterator_t *iterator; | |
128 | ||
129 | iterator = this->private_keys->create_iterator(this->private_keys, TRUE); | |
130 | while (iterator->has_next(iterator)) | |
131 | { | |
132 | iterator->current(iterator, (void**)¤t); | |
133 | if (identification->equals(identification, current->id)) | |
134 | { | |
135 | found = current->key->clone(current->key); | |
136 | break; | |
137 | } | |
138 | } | |
139 | iterator->destroy(iterator); | |
140 | return found; | |
141 | } | |
142 | ||
143 | /** | |
9820c0e2 | 144 | * Implements local_credential_store_t.load_certificates |
13e4a62f | 145 | */ |
9820c0e2 | 146 | static void load_certificates(private_local_credential_store_t *this, const char *path) |
13e4a62f MW |
147 | { |
148 | struct dirent* entry; | |
149 | struct stat stb; | |
150 | DIR* dir; | |
151 | x509_t *cert; | |
152 | ||
153 | dir = opendir(path); | |
154 | if (dir == NULL) { | |
155 | this->logger->log(this->logger, ERROR, "error opening certificate directory \"%s\"", path); | |
156 | return; | |
157 | } | |
158 | while ((entry = readdir(dir)) != NULL) | |
159 | { | |
9820c0e2 MW |
160 | char file[PATH_BUF]; |
161 | ||
13e4a62f MW |
162 | snprintf(file, sizeof(file), "%s/%s", path, entry->d_name); |
163 | ||
164 | if (stat(file, &stb) == -1) | |
165 | { | |
166 | continue; | |
167 | } | |
168 | /* try to parse all regular files */ | |
169 | if (stb.st_mode & S_IFREG) | |
170 | { | |
171 | cert = x509_create_from_file(file); | |
172 | if (cert) | |
173 | { | |
174 | this->certificates->insert_last(this->certificates, (void*)cert); | |
13e4a62f MW |
175 | } |
176 | else | |
177 | { | |
178 | this->logger->log(this->logger, ERROR, "certificate \"%s\" invalid, skipped", file); | |
179 | } | |
180 | } | |
181 | } | |
182 | closedir(dir); | |
183 | } | |
184 | ||
185 | /** | |
186 | * Query the ID for a private key, by doing a lookup in the certificates | |
187 | */ | |
188 | static identification_t *get_id_for_private_key(private_local_credential_store_t *this, rsa_private_key_t *private_key) | |
189 | { | |
190 | iterator_t *iterator; | |
191 | x509_t *cert; | |
192 | identification_t *found = NULL; | |
193 | rsa_public_key_t *public_key; | |
194 | ||
195 | this->logger->log(this->logger, CONTROL|LEVEL2, "Getting ID for a private key..."); | |
196 | ||
197 | iterator = this->certificates->create_iterator(this->certificates, TRUE); | |
198 | while (!found && iterator->has_next(iterator)) | |
199 | { | |
200 | iterator->current(iterator, (void**)&cert); | |
201 | public_key = cert->get_public_key(cert); | |
202 | if (public_key) | |
203 | { | |
204 | if (private_key->belongs_to(private_key, public_key)) | |
205 | { | |
206 | this->logger->log(this->logger, CONTROL|LEVEL2, "found a match"); | |
207 | found = cert->get_subject(cert); | |
208 | found = found->clone(found); | |
209 | } | |
210 | else | |
211 | { | |
212 | this->logger->log(this->logger, CONTROL|LEVEL3, "this one did not match"); | |
213 | } | |
214 | public_key->destroy(public_key); | |
215 | } | |
216 | } | |
217 | iterator->destroy(iterator); | |
218 | return found; | |
219 | } | |
220 | ||
221 | /** | |
222 | * Implements local_credential_store_t.load_private_keys | |
223 | */ | |
9820c0e2 | 224 | static void load_private_keys(private_local_credential_store_t *this, const char *secretsfile, const char *defaultpath) |
13e4a62f | 225 | { |
9820c0e2 MW |
226 | FILE *fd = fopen(secretsfile, "r"); |
227 | ||
228 | if (fd) | |
13e4a62f | 229 | { |
9820c0e2 MW |
230 | int bytes; |
231 | int line_nr = 0; | |
232 | chunk_t chunk, src, line; | |
233 | ||
234 | this->logger->log(this->logger, CONTROL, "loading secrets from \"%s\"", secretsfile); | |
235 | ||
236 | fseek(fd, 0, SEEK_END); | |
237 | chunk.len = ftell(fd); | |
238 | rewind(fd); | |
239 | chunk.ptr = malloc(chunk.len); | |
240 | bytes = fread(chunk.ptr, 1, chunk.len, fd); | |
241 | fclose(fd); | |
242 | ||
243 | src = chunk; | |
244 | ||
245 | while (fetchline(&src, &line)) | |
13e4a62f | 246 | { |
9820c0e2 MW |
247 | chunk_t ids, token; |
248 | ||
249 | line_nr++; | |
250 | ||
251 | if (!eat_whitespace(&line)) | |
13e4a62f | 252 | { |
9820c0e2 MW |
253 | continue; |
254 | } | |
255 | if (!extract_token(&ids, ':', &line)) | |
256 | { | |
257 | this->logger->log(this->logger, ERROR, "line %d: missing ':' separator", line_nr); | |
258 | goto error; | |
259 | } | |
260 | if (!eat_whitespace(&line) || !extract_token(&token, ' ', &line)) | |
261 | { | |
262 | this->logger->log(this->logger, ERROR, "line %d: missing token", line_nr); | |
263 | goto error; | |
264 | } | |
265 | if (match("RSA", &token)) | |
266 | { | |
267 | char path[PATH_BUF]; | |
268 | chunk_t filename; | |
269 | ||
270 | err_t ugh = extract_value(&filename, &line); | |
271 | ||
272 | if (ugh != NULL) | |
273 | { | |
274 | this->logger->log(this->logger, ERROR, "line %d: %s", line_nr, ugh); | |
275 | goto error; | |
276 | } | |
277 | if (filename.len == 0) | |
278 | { | |
279 | this->logger->log(this->logger, ERROR, | |
280 | "line %d: empty filename", line_nr); | |
281 | goto error; | |
282 | } | |
283 | if (*filename.ptr == '/') | |
284 | { | |
285 | /* absolute path name */ | |
286 | snprintf(path, sizeof(path), "%.*s", filename.len, filename.ptr); | |
287 | } | |
288 | else | |
13e4a62f | 289 | { |
9820c0e2 MW |
290 | /* relative path name */ |
291 | snprintf(path, sizeof(path), "%s/%.*s", defaultpath, filename.len, filename.ptr); | |
13e4a62f | 292 | } |
9820c0e2 MW |
293 | |
294 | rsa_private_key_t *key = rsa_private_key_create_from_file(path, NULL); | |
295 | if (key) | |
296 | { | |
297 | key_entry_t *entry; | |
298 | identification_t *id = get_id_for_private_key(this, key); | |
299 | ||
300 | if (!id) | |
301 | { | |
302 | this->logger->log(this->logger, ERROR, | |
303 | "no certificate found for private key \"%s\", skipped", path); | |
304 | key->destroy(key); | |
305 | continue; | |
306 | } | |
307 | entry = malloc_thing(key_entry_t); | |
308 | entry->key = key; | |
309 | entry->id = id; | |
310 | this->private_keys->insert_last(this->private_keys, (void*)entry); | |
311 | } | |
312 | } | |
313 | else if (match("PSK", &token)) | |
314 | { | |
315 | ||
316 | } | |
317 | else if (match("PIN", &token)) | |
318 | { | |
319 | ||
13e4a62f MW |
320 | } |
321 | else | |
322 | { | |
9820c0e2 MW |
323 | this->logger->log(this->logger, ERROR, |
324 | "line %d: token must be either RSA, PSK, or PIN", | |
325 | line_nr, token.len); | |
326 | goto error; | |
13e4a62f MW |
327 | } |
328 | } | |
9820c0e2 MW |
329 | error: |
330 | free(chunk.ptr); | |
331 | } | |
332 | else | |
333 | { | |
334 | this->logger->log(this->logger, ERROR, "could not open file '%s'", secretsfile); | |
13e4a62f | 335 | } |
13e4a62f MW |
336 | } |
337 | ||
338 | /** | |
339 | * Implementation of credential_store_t.destroy. | |
340 | */ | |
341 | static void destroy(private_local_credential_store_t *this) | |
342 | { | |
343 | x509_t *certificate; | |
344 | key_entry_t *key_entry; | |
345 | ||
346 | while (this->certificates->remove_last(this->certificates, (void**)&certificate) == SUCCESS) | |
347 | { | |
348 | certificate->destroy(certificate); | |
349 | } | |
350 | this->certificates->destroy(this->certificates); | |
351 | while (this->private_keys->remove_last(this->private_keys, (void**)&key_entry) == SUCCESS) | |
352 | { | |
353 | key_entry->id->destroy(key_entry->id); | |
354 | key_entry->key->destroy(key_entry->key); | |
355 | free(key_entry); | |
356 | } | |
357 | this->private_keys->destroy(this->private_keys); | |
358 | free(this); | |
359 | } | |
360 | ||
361 | /** | |
362 | * Described in header. | |
363 | */ | |
364 | local_credential_store_t * local_credential_store_create() | |
365 | { | |
366 | private_local_credential_store_t *this = malloc_thing(private_local_credential_store_t); | |
367 | ||
368 | this->public.credential_store.get_shared_secret = (status_t(*)(credential_store_t*,identification_t*,chunk_t*))get_shared_secret; | |
369 | this->public.credential_store.get_rsa_private_key = (rsa_private_key_t*(*)(credential_store_t*,identification_t*))get_rsa_private_key; | |
370 | this->public.credential_store.get_rsa_public_key = (rsa_public_key_t*(*)(credential_store_t*,identification_t*))get_rsa_public_key; | |
9820c0e2 MW |
371 | this->public.load_certificates = (void(*)(local_credential_store_t*,const char*))load_certificates; |
372 | this->public.load_private_keys = (void(*)(local_credential_store_t*,const char*, const char*))load_private_keys; | |
13e4a62f MW |
373 | this->public.credential_store.destroy = (void(*)(credential_store_t*))destroy; |
374 | ||
375 | /* private variables */ | |
376 | this->private_keys = linked_list_create(); | |
377 | this->certificates = linked_list_create(); | |
378 | this->logger = logger_manager->get_logger(logger_manager, CONFIG); | |
379 | ||
380 | return (&this->public); | |
381 | } |