Return to coupling_validator.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / strongswan / src / libcharon / plugins / coupling |
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: }