Use centralized hasher names in coupling plugin
[strongswan.git] / src / libcharon / plugins / coupling / coupling_validator.c
1 /*
2 * Copyright (C) 2011 Martin Willi
3 * Copyright (C) 2011 revosec AG
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "coupling_validator.h"
17
18 #include <errno.h>
19 #include <time.h>
20
21 #include <daemon.h>
22 #include <threading/mutex.h>
23
24 /* buffer size for hex-encoded hash */
25 #define MAX_HASH_SIZE (HASH_SIZE_SHA512 * 2 + 1)
26
27 typedef struct private_coupling_validator_t private_coupling_validator_t;
28
29 /**
30 * Private data of an coupling_validator_t object.
31 */
32 struct private_coupling_validator_t {
33
34 /**
35 * Public coupling_validator_t interface.
36 */
37 coupling_validator_t public;
38
39 /**
40 * Mutex
41 */
42 mutex_t *mutex;
43
44 /**
45 * File with device couplings
46 */
47 FILE *f;
48
49 /**
50 * Hasher to create hashes
51 */
52 hasher_t *hasher;
53
54 /**
55 * maximum number of couplings
56 */
57 int max_couplings;
58 };
59
60 /**
61 * Get hash of a certificate
62 */
63 static bool get_cert_hash(private_coupling_validator_t *this,
64 certificate_t *cert, char *hex)
65 {
66 char buf[MAX_HASH_SIZE];
67 chunk_t encoding;
68
69 if (!cert->get_encoding(cert, CERT_ASN1_DER, &encoding))
70 {
71 return FALSE;
72 }
73 if (!this->hasher->get_hash(this->hasher, encoding, buf))
74 {
75 free(encoding.ptr);
76 return FALSE;
77 }
78 free(encoding.ptr);
79 chunk_to_hex(chunk_create(buf, this->hasher->get_hash_size(this->hasher)),
80 hex, FALSE);
81 return TRUE;
82 }
83
84 /**
85 * Check if we have an entry for a given hash
86 */
87 static bool has_entry(private_coupling_validator_t *this, char *hash)
88 {
89 char line[256];
90 int hash_len;
91
92 hash_len = strlen(hash);
93 rewind(this->f);
94
95 while (fgets(line, sizeof(line), this->f))
96 {
97 if (strlen(line) >= hash_len &&
98 strncaseeq(line, hash, hash_len))
99 {
100 return TRUE;
101 }
102 }
103 return FALSE;
104 }
105
106 /**
107 * Get the number of coupling entries we currently have
108 */
109 static int get_number_of_entries(private_coupling_validator_t *this)
110 {
111 char line[256];
112 int count = 0;
113
114 rewind(this->f);
115
116 while (fgets(line, sizeof(line), this->f))
117 {
118 /* valid entries start with hex encoded hash */
119 if (strchr("1234567890abcdefABCDEF", line[0]))
120 {
121 count++;
122 }
123 }
124 return count;
125 }
126
127 /**
128 * Add a new entry to the file
129 */
130 static bool add_entry(private_coupling_validator_t *this, char *hash,
131 identification_t *id)
132 {
133 return fseek(this->f, 0, SEEK_END) == 0 &&
134 fprintf(this->f, "%s %u '%Y'\n", hash, time(NULL), id) > 0;
135 }
136
137 METHOD(cert_validator_t, validate, bool,
138 private_coupling_validator_t *this,
139 certificate_t *subject, certificate_t *issuer,
140 bool online, u_int pathlen, bool anchor, auth_cfg_t *auth)
141 {
142 bool valid = FALSE;
143 char hash[MAX_HASH_SIZE];
144
145 if (pathlen != 0)
146 {
147 return TRUE;
148 }
149 if (get_cert_hash(this, subject, hash))
150 {
151 this->mutex->lock(this->mutex);
152 if (has_entry(this, hash))
153 {
154 DBG1(DBG_CFG, "coupled certificate '%Y' found, accepted",
155 subject->get_subject(subject));
156 valid = TRUE;
157 }
158 else if (get_number_of_entries(this) < this->max_couplings)
159 {
160 if (add_entry(this, hash, subject->get_subject(subject)))
161 {
162 DBG1(DBG_CFG, "coupled new certificate '%Y'",
163 subject->get_subject(subject));
164 valid = TRUE;
165 }
166 else
167 {
168 DBG1(DBG_CFG, "coupling new certificate '%Y' failed",
169 subject->get_subject(subject));
170 }
171 }
172 else
173 {
174 DBG1(DBG_CFG, "coupling new certificate '%Y' failed, limit of %d "
175 "couplings reached", subject->get_subject(subject),
176 this->max_couplings);
177 }
178 this->mutex->unlock(this->mutex);
179 }
180 return valid;
181 }
182
183 METHOD(coupling_validator_t, destroy, void,
184 private_coupling_validator_t *this)
185 {
186 if (this->f)
187 {
188 fclose(this->f);
189 }
190 DESTROY_IF(this->hasher);
191 this->mutex->destroy(this->mutex);
192 free(this);
193 }
194
195 /**
196 * See header
197 */
198 coupling_validator_t *coupling_validator_create()
199 {
200 private_coupling_validator_t *this;
201 char *path, *hash;
202
203 INIT(this,
204 .public = {
205 .validator = {
206 .validate = _validate,
207 },
208 .destroy = _destroy,
209 },
210 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
211 .max_couplings = lib->settings->get_int(lib->settings,
212 "%s.plugins.coupling.max", 1,
213 charon->name),
214 );
215
216 hash = lib->settings->get_str(lib->settings,
217 "%s.plugins.coupling.hash", "sha1",
218 charon->name);
219 this->hasher = lib->crypto->create_hasher(lib->crypto,
220 enum_from_name(hash_algorithm_short_names, hash));
221 if (!this->hasher)
222 {
223 DBG1(DBG_CFG, "unsupported coupling hash algorithm: %s", hash);
224 destroy(this);
225 return NULL;
226 }
227
228 path = lib->settings->get_str(lib->settings,
229 "%s.plugins.coupling.file", NULL,
230 charon->name);
231 if (!path)
232 {
233 DBG1(DBG_CFG, "coupling file path unspecified");
234 destroy(this);
235 return NULL;
236 }
237 this->f = fopen(path, "a+");
238 if (!this->f)
239 {
240 DBG1(DBG_CFG, "opening coupling file '%s' failed: %s",
241 path, strerror(errno));
242 destroy(this);
243 return NULL;
244 }
245 setlinebuf(this->f);
246 return &this->public;
247 }