|
|
1.1 misho 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: hash_algorithm_t alg;
206: char *path, *hash;
207:
208: INIT(this,
209: .public = {
210: .validator = {
211: .validate = _validate,
212: },
213: .destroy = _destroy,
214: },
215: .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
216: .max_couplings = lib->settings->get_int(lib->settings,
217: "%s.plugins.coupling.max", 1,
218: lib->ns),
219: );
220:
221: hash = lib->settings->get_str(lib->settings,
222: "%s.plugins.coupling.hash", "sha1", lib->ns);
223: if (!enum_from_name(hash_algorithm_short_names, hash, &alg))
224: {
225: DBG1(DBG_CFG, "unknown coupling hash algorithm: %s", hash);
226: destroy(this);
227: return NULL;
228: }
229: this->hasher = lib->crypto->create_hasher(lib->crypto, alg);
230: if (!this->hasher)
231: {
232: DBG1(DBG_CFG, "unsupported coupling hash algorithm: %s", hash);
233: destroy(this);
234: return NULL;
235: }
236:
237: path = lib->settings->get_str(lib->settings,
238: "%s.plugins.coupling.file", NULL, lib->ns);
239: if (!path)
240: {
241: DBG1(DBG_CFG, "coupling file path unspecified");
242: destroy(this);
243: return NULL;
244: }
245: this->f = fopen(path, "a+");
246: if (!this->f)
247: {
248: DBG1(DBG_CFG, "opening coupling file '%s' failed: %s",
249: path, strerror(errno));
250: destroy(this);
251: return NULL;
252: }
253: setlinebuf(this->f);
254: return &this->public;
255: }