coupling: Fix call to call_hook()
[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 lib->credmgr->call_hook(lib->credmgr,
171 CRED_HOOK_POLICY_VIOLATION, subject);
172 }
173 }
174 else
175 {
176 DBG1(DBG_CFG, "coupling new certificate '%Y' failed, limit of %d "
177 "couplings reached", subject->get_subject(subject),
178 this->max_couplings);
179 lib->credmgr->call_hook(lib->credmgr, CRED_HOOK_POLICY_VIOLATION,
180 subject);
181 }
182 this->mutex->unlock(this->mutex);
183 }
184 return valid;
185 }
186
187 METHOD(coupling_validator_t, destroy, void,
188 private_coupling_validator_t *this)
189 {
190 if (this->f)
191 {
192 fclose(this->f);
193 }
194 DESTROY_IF(this->hasher);
195 this->mutex->destroy(this->mutex);
196 free(this);
197 }
198
199 /**
200 * See header
201 */
202 coupling_validator_t *coupling_validator_create()
203 {
204 private_coupling_validator_t *this;
205 char *path, *hash;
206
207 INIT(this,
208 .public = {
209 .validator = {
210 .validate = _validate,
211 },
212 .destroy = _destroy,
213 },
214 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
215 .max_couplings = lib->settings->get_int(lib->settings,
216 "%s.plugins.coupling.max", 1,
217 charon->name),
218 );
219
220 hash = lib->settings->get_str(lib->settings,
221 "%s.plugins.coupling.hash", "sha1",
222 charon->name);
223 this->hasher = lib->crypto->create_hasher(lib->crypto,
224 enum_from_name(hash_algorithm_short_names, hash));
225 if (!this->hasher)
226 {
227 DBG1(DBG_CFG, "unsupported coupling hash algorithm: %s", hash);
228 destroy(this);
229 return NULL;
230 }
231
232 path = lib->settings->get_str(lib->settings,
233 "%s.plugins.coupling.file", NULL,
234 charon->name);
235 if (!path)
236 {
237 DBG1(DBG_CFG, "coupling file path unspecified");
238 destroy(this);
239 return NULL;
240 }
241 this->f = fopen(path, "a+");
242 if (!this->f)
243 {
244 DBG1(DBG_CFG, "opening coupling file '%s' failed: %s",
245 path, strerror(errno));
246 destroy(this);
247 return NULL;
248 }
249 setlinebuf(this->f);
250 return &this->public;
251 }