zidCache.c 16.5 KB
Newer Older
johan's avatar
johan committed
1 2 3 4 5 6
/**
 @file zidCache.c

 @brief all ZID and cache related operations are implemented in this file
 - get or create ZID
 - get/update associated secrets
7
 It supports cacheless implementation when cache file access functions are null
johan's avatar
johan committed
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27

 @author Johan Pascal

 @copyright Copyright (C) 2014 Belledonne Communications, Grenoble, France
 
 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 as published by the Free Software Foundation; either version 2
 of the License, or (at your option) any later version.
 
 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
#include <stdlib.h>
johan's avatar
johan committed
28
#include <string.h>
29
#include "typedef.h"
30
#include <bctoolbox/crypto.h>
31
#include "cryptoUtils.h"
32
#include "zidCache.h"
33

34 35
#ifdef HAVE_LIBXML2

johan's avatar
johan committed
36 37
#include <libxml/tree.h>
#include <libxml/parser.h>
johan's avatar
johan committed
38

johan's avatar
johan committed
39 40 41 42
#define MIN_VALID_CACHE_LENGTH 56 /* root tag + selfZID tag size */
#define XML_HEADER_STRING "<?xml version='1.0' encoding='utf-8'?>"
#define XML_HEADER_SIZE 38
/* Local functions prototypes */
43
static void bzrtp_writeCache(bzrtpContext_t *zrtpContext);
johan's avatar
johan committed
44

45
int bzrtp_getSelfZID(bzrtpContext_t *context, uint8_t selfZID[12]) {
46 47
	uint8_t *selfZidHex = NULL;

johan's avatar
johan committed
48 49 50
	if (context == NULL) {
		return ZRTP_ZIDCACHE_INVALID_CONTEXT; 
	}
johan's avatar
johan committed
51
	/* load the cache buffer and parse it to an xml doc. TODO: lock it as we may write it */
johan's avatar
johan committed
52
	if (context->zrtpCallbacks.bzrtp_loadCache != NULL) {
johan's avatar
johan committed
53 54
		uint8_t *cacheStringBuffer;
		uint32_t cacheStringLength;
Simon Morlat's avatar
Simon Morlat committed
55
		zrtpFreeBuffer_callback cb=NULL;
56
		context->zrtpCallbacks.bzrtp_loadCache(context->ZIDCacheData, &cacheStringBuffer, &cacheStringLength, &cb);
johan's avatar
johan committed
57
		context->cacheBuffer = xmlParseDoc(cacheStringBuffer);
Simon Morlat's avatar
Simon Morlat committed
58
		if (cb!=NULL) cb(cacheStringBuffer);
59 60
	} else {
		/* we are running cacheless, return a random number */
61
		bctoolbox_rng_get(context->RNGContext, selfZID, 12);
62
		return 0; 
johan's avatar
johan committed
63 64
	}

johan's avatar
johan committed
65 66
	if (context->cacheBuffer != NULL ) { /* there is a cache, try to find our ZID */
		xmlNodePtr cur = xmlDocGetRootElement(context->cacheBuffer);
johan's avatar
johan committed
67 68 69 70 71 72 73
		/* if we found a root element, parse its children node */
		if (cur!=NULL) 
		{
			cur = cur->xmlChildrenNode;
		}
		while (cur!=NULL) {
			if ((!xmlStrcmp(cur->name, (const xmlChar *)"selfZID"))){ /* self ZID found, extract it */
johan's avatar
johan committed
74
				selfZidHex = xmlNodeListGetString(context->cacheBuffer, cur->xmlChildrenNode, 1);		
johan's avatar
johan committed
75 76 77 78 79
				/* convert it from hexa string to bytes string */
				bzrtp_strToUint8(selfZID, selfZidHex, strlen((char *)selfZidHex));
				break;
			}
			cur = cur->next;
johan's avatar
johan committed
80 81 82
		}
	}

johan's avatar
johan committed
83 84 85

	/* if we didn't found anything in cache, or we have no cache at all: generate ZID, cache string and write it to file */
	if (selfZidHex==NULL) {
86 87 88
		uint8_t newZidHex[25];
		xmlNodePtr rootNode;

johan's avatar
johan committed
89
		/* generate a random ZID */
90
		bctoolbox_rng_get(context->RNGContext, selfZID, 12);
johan's avatar
johan committed
91 92 93
		/* convert it to an Hexa String */
		bzrtp_int8ToStr(newZidHex, selfZID, 12);
		newZidHex[24] = '\0'; /* the string must be null terminated for libxml2 to add it correctly in the element */
johan's avatar
johan committed
94
		xmlFree(context->cacheBuffer);
johan's avatar
johan committed
95
		/* Create a new xml doc */
johan's avatar
johan committed
96
		context->cacheBuffer = xmlNewDoc((const xmlChar *)"1.0");
johan's avatar
johan committed
97
		/* root tag is "cache" */
98
		rootNode = xmlNewDocNode(context->cacheBuffer, NULL, (const xmlChar *)"cache", NULL);
johan's avatar
johan committed
99
	    xmlDocSetRootElement(context->cacheBuffer, rootNode);
johan's avatar
johan committed
100 101 102 103
		/* add the ZID child */
		xmlNewTextChild(rootNode, NULL, (const xmlChar *)"selfZID", newZidHex);
		
		/* write the cache file and unlock it(TODO)*/
johan's avatar
johan committed
104
		bzrtp_writeCache(context);
johan's avatar
johan committed
105 106 107 108
	}
	/* TODO unlock the cache */
	xmlFree(selfZidHex);

johan's avatar
johan committed
109 110 111 112
	return 0;
}

/**
johan's avatar
johan committed
113
 * @brief Parse the cache to find secrets associated to the given ZID, set them and their length in the context if they are found 
johan's avatar
johan committed
114
 *
johan's avatar
johan committed
115 116
 * @param[in/out]	context		the current context, used to get the negotiated Hash algorithm and cache access functions and store result
 * @param[in]		peerZID		a byte array of the peer ZID
johan's avatar
johan committed
117
 *
johan's avatar
johan committed
118
 * return 	0 on succes, error code otherwise 
johan's avatar
johan committed
119
 */
johan's avatar
johan committed
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
int bzrtp_getPeerAssociatedSecretsHash(bzrtpContext_t *context, uint8_t peerZID[12]) {
	if (context == NULL) {
		return ZRTP_ZIDCACHE_INVALID_CONTEXT;
	}

	/* resert cached secret buffer */
	free(context->cachedSecret.rs1);
	free(context->cachedSecret.rs2);
	free(context->cachedSecret.pbxsecret);
	free(context->cachedSecret.auxsecret);
	context->cachedSecret.rs1 = NULL;
	context->cachedSecret.rs1Length = 0;
	context->cachedSecret.rs2 = NULL;
	context->cachedSecret.rs2Length = 0;
	context->cachedSecret.pbxsecret = NULL;
	context->cachedSecret.pbxsecretLength = 0;
	context->cachedSecret.auxsecret = NULL;
	context->cachedSecret.auxsecretLength = 0;
	context->cachedSecret.previouslyVerifiedSas = 0;

	/* parse the cache to find the peer element matching the given ZID */
johan's avatar
johan committed
141
	if (context->cacheBuffer != NULL ) { /* there is a cache, try to find our peer element */
johan's avatar
johan committed
142
		uint8_t peerZidHex[24];
143 144
		xmlNodePtr cur;

johan's avatar
johan committed
145 146
		bzrtp_int8ToStr(peerZidHex, peerZID, 12); /* compute the peerZID as an Hexa string */

147
		cur = xmlDocGetRootElement(context->cacheBuffer);
johan's avatar
johan committed
148 149 150 151 152 153 154
		/* if we found a root element, parse its children node */
		if (cur!=NULL) 
		{
			cur = cur->xmlChildrenNode;
		}
		while (cur!=NULL) {
			if ((!xmlStrcmp(cur->name, (const xmlChar *)"peer"))){ /* found a peer, check his ZID element */
johan's avatar
johan committed
155
				xmlChar *currentZidHex = xmlNodeListGetString(context->cacheBuffer, cur->xmlChildrenNode->xmlChildrenNode, 1); /* ZID is the first element of peer */
johan's avatar
johan committed
156 157 158
				if (memcmp(currentZidHex, peerZidHex, 24) == 0) { /* we found the peer element we are looking for */
					xmlNodePtr peerNode = cur->xmlChildrenNode->next; /* no need to parse the first child as it is the ZID node */
					while (peerNode != NULL) { /* get all the needed information : rs1, rs2, pbx and aux if we found them */
johan's avatar
johan committed
159
						xmlChar *nodeContent = NULL;
johan's avatar
johan committed
160
						if (!xmlStrcmp(peerNode->name, (const xmlChar *)"rs1")) {
johan's avatar
johan committed
161
							nodeContent = xmlNodeListGetString(context->cacheBuffer, peerNode->xmlChildrenNode, 1);
johan's avatar
johan committed
162 163 164 165 166
							context->cachedSecret.rs1 = (uint8_t *)malloc(RETAINED_SECRET_LENGTH);
							context->cachedSecret.rs1Length = RETAINED_SECRET_LENGTH;
							bzrtp_strToUint8(context->cachedSecret.rs1, nodeContent, 2*RETAINED_SECRET_LENGTH); /* RETAINED_SECRET_LENGTH is in byte, the nodeContent buffer is in hexa string so twice the length of byte string */
						}
						if (!xmlStrcmp(peerNode->name, (const xmlChar *)"rs2")) {
johan's avatar
johan committed
167
							nodeContent = xmlNodeListGetString(context->cacheBuffer, peerNode->xmlChildrenNode, 1);
johan's avatar
johan committed
168 169 170 171 172
							context->cachedSecret.rs2 = (uint8_t *)malloc(RETAINED_SECRET_LENGTH);
							context->cachedSecret.rs2Length = RETAINED_SECRET_LENGTH;
							bzrtp_strToUint8(context->cachedSecret.rs2, nodeContent, 2*RETAINED_SECRET_LENGTH); /* RETAINED_SECRET_LENGTH is in byte, the nodeContent buffer is in hexa string so twice the length of byte string */
						}
						if (!xmlStrcmp(peerNode->name, (const xmlChar *)"aux")) {
johan's avatar
johan committed
173
							nodeContent = xmlNodeListGetString(context->cacheBuffer, peerNode->xmlChildrenNode, 1);
johan's avatar
johan committed
174 175 176 177 178
							context->cachedSecret.auxsecretLength = strlen((const char *)nodeContent)/2;
							context->cachedSecret.auxsecret = (uint8_t *)malloc(context->cachedSecret.auxsecretLength); /* aux secret is of user defined length, node Content is an hexa string */
							bzrtp_strToUint8(context->cachedSecret.auxsecret, nodeContent, 2*context->cachedSecret.auxsecretLength); 
						}
						if (!xmlStrcmp(peerNode->name, (const xmlChar *)"pbx")) {
johan's avatar
johan committed
179
							nodeContent = xmlNodeListGetString(context->cacheBuffer, peerNode->xmlChildrenNode, 1);
johan's avatar
johan committed
180 181 182 183 184
							context->cachedSecret.pbxsecret = (uint8_t *)malloc(RETAINED_SECRET_LENGTH);
							context->cachedSecret.pbxsecretLength = RETAINED_SECRET_LENGTH;
							bzrtp_strToUint8(context->cachedSecret.pbxsecret, nodeContent, 2*RETAINED_SECRET_LENGTH); /* RETAINED_SECRET_LENGTH is in byte, the nodeContent buffer is in hexa string so twice the length of byte string */
						}
						if (!xmlStrcmp(peerNode->name, (const xmlChar *)"pvs")) { /* this one is the previously verified sas flag */
johan's avatar
johan committed
185
							nodeContent = xmlNodeListGetString(context->cacheBuffer, peerNode->xmlChildrenNode, 1);
johan's avatar
johan committed
186 187 188 189 190 191
							if (nodeContent[1] == *"1") { /* pvs is a boolean but is stored as a byte, on 2 hex chars */
								context->cachedSecret.previouslyVerifiedSas = 1;
							}
						}
						xmlFree(nodeContent);
						peerNode = peerNode->next;
johan's avatar
johan committed
192
					}
johan's avatar
johan committed
193 194
					xmlFree(currentZidHex);
					currentZidHex=NULL;
johan's avatar
johan committed
195
					break;
johan's avatar
johan committed
196
				}
197 198
				xmlFree(currentZidHex);
				currentZidHex=NULL;
johan's avatar
johan committed
199
			}
johan's avatar
johan committed
200
			cur = cur->next;
johan's avatar
johan committed
201 202
		}
	}
johan's avatar
johan committed
203 204

	return 0;
johan's avatar
johan committed
205 206
}

207
/**
johan's avatar
johan committed
208 209
 * @brief Write the given taf into peer Node, if the tag exists, content is replaced
 * Cache file is locked(TODO), read and updated during this call
210
 *
johan's avatar
johan committed
211 212 213 214 215
 * @param[in/out]	context				the current context, used to get the negotiated Hash algorithm and cache access functions and store result
 * @param[in]		peerZID				a byte array of the peer ZID
 * @param[in]		tagName				the tagname of node to be written, it MUST be null terminated
 * @param[in]		tagNameLength		the length of tagname (not including the null termination char)
 * @param[in]		tagContent			the content of the node(a byte buffer which will be converted to hexa string)
216 217 218 219 220
 * @param[in]		tagContentLength	the length of the content to be written(not including the null termination)
 * @param[in]		nodeFlag			Flag, if the ISSTRING bit is set write directly the value into the tag, otherwise convert the byte buffer to hexa string
 * 										if the MULTIPLETAGS bit is set, allow multiple tags with the same name inside the peer node(only if their value differs)
 * @param[in]		fileFlag			Flag, if LOADFILE bit is set, reload the cache buffer from file before updatin.
 * 										if WRITEFILE bit is set, update the cache file
johan's avatar
johan committed
221
 * 
222 223
 * Note : multiple tags mode manage string content only
 *
johan's avatar
johan committed
224
 * return 0 on success, error code otherwise
225
 */
226
int bzrtp_writePeerNode(bzrtpContext_t *context, uint8_t peerZID[12], uint8_t *tagName, uint8_t tagNameLength, uint8_t *tagContent, uint32_t tagContentLength, uint8_t nodeFlag, uint8_t fileFlag) {
227 228
	uint8_t *tagContentHex; /* this one will store the actual value to be written in cache */

johan's avatar
johan committed
229 230 231
	if ((context == NULL) || (context->zrtpCallbacks.bzrtp_loadCache == NULL)) {
		return ZRTP_ZIDCACHE_INVALID_CONTEXT;
	}
232

233 234 235 236 237 238 239 240 241
	if ((nodeFlag&BZRTP_CACHE_ISSTRINGBIT) == BZRTP_CACHE_TAGISBYTE) { /* tag content is a byte buffer, convert it to hexa string */
		/* turn the tagContent to an hexa string null terminated */
		tagContentHex = (uint8_t *)malloc(2*tagContentLength+1);
		bzrtp_int8ToStr(tagContentHex, tagContent, tagContentLength);
		tagContentHex[2*tagContentLength] = '\0';
	} else { /* tag content is a string, write it directly */
		tagContentHex = (uint8_t *)malloc(tagContentLength+1);
		memcpy(tagContentHex, tagContent, tagContentLength+1); /* duplicate the string to have it in the same variable in both case and be able to free it at the end */
	}
johan's avatar
johan committed
242

243
	if ((fileFlag&BZRTP_CACHE_LOADFILEBIT) == BZRTP_CACHE_LOADFILE) { /* we must reload the cache from file */
244 245
		uint8_t *cacheStringBuffer;
		uint32_t cacheStringLength;
Simon Morlat's avatar
Simon Morlat committed
246
		zrtpFreeBuffer_callback cb=NULL;
247

248
		/* reload cache from file locking it (TODO: lock) */
johan's avatar
johan committed
249
		xmlFreeDoc(context->cacheBuffer);
250
		context->cacheBuffer = NULL;
251
		context->zrtpCallbacks.bzrtp_loadCache(context->ZIDCacheData, &cacheStringBuffer, &cacheStringLength,&cb);
johan's avatar
johan committed
252
		context->cacheBuffer = xmlParseDoc(cacheStringBuffer);
Simon Morlat's avatar
Simon Morlat committed
253
		if (cb) cb(cacheStringBuffer);
254
	}
johan's avatar
johan committed
255 256

	/* parse the cache to find the peer element matching the given ZID */
johan's avatar
johan committed
257
	if (context->cacheBuffer != NULL ) { /* there is a cache, try to find our peer element */
johan's avatar
johan committed
258
		uint8_t peerZidHex[25];
259 260 261 262
		xmlNodePtr rootNode;
		xmlNodePtr cur = NULL;
		uint8_t nodeUpdated = 0; /* a boolean flag set if node is updated */

johan's avatar
johan committed
263 264 265
		bzrtp_int8ToStr(peerZidHex, peerZID, 12); /* compute the peerZID as an Hexa string */
		peerZidHex[24]='\0';

266
		rootNode = xmlDocGetRootElement(context->cacheBuffer);
johan's avatar
johan committed
267 268 269 270 271 272 273
		/* if we found a root element, parse its children node */
		if (rootNode!=NULL) 
		{
			cur = rootNode->xmlChildrenNode->next; /* first node is selfZID, don't parse it */
		}
		while (cur!=NULL) {
			if ((!xmlStrcmp(cur->name, (const xmlChar *)"peer"))){ /* found a peer, check his ZID element */
johan's avatar
johan committed
274 275
				xmlChar *currentZidHex = xmlNodeListGetString(context->cacheBuffer, cur->xmlChildrenNode->xmlChildrenNode, 1); /* ZID is the first element of peer */
				if (!xmlStrcmp(currentZidHex, (const xmlChar *)peerZidHex)) { /* we found the peer element we are looking for */
276 277 278 279 280
					xmlNodePtr peerNodeChildren = cur->xmlChildrenNode->next;
					while (peerNodeChildren != NULL && nodeUpdated==0) { /* look for the tag we want to write */
						if ((!xmlStrcmp(peerNodeChildren->name, (const xmlChar *)tagName))){ /* check if we already have the tag we want to write */
							if ((nodeFlag&BZRTP_CACHE_MULTIPLETAGSBIT) == BZRTP_CACHE_ALLOWMULTIPLETAGS) { /* multiple nodes with the same name are allowed, check the current one have a different value */
								/* check if the node found have the same content than the one we want to add */
johan's avatar
johan committed
281
								xmlChar *currentNodeContent = xmlNodeListGetString(context->cacheBuffer, peerNodeChildren->xmlChildrenNode, 1);
282 283 284 285 286 287 288 289 290 291
								if (!xmlStrcmp((const xmlChar *)currentNodeContent, (const xmlChar *)tagContent)) { /* contents are the same, do nothing and get out */
									nodeUpdated = 1;
								} else { /* tagname is the same but content differs, keep on parsing this peer node */
									peerNodeChildren = peerNodeChildren->next;
								}
								xmlFree(currentNodeContent);
							} else { /* no multiple tags with the same name allowed, overwrite the content in any case */
								xmlNodeSetContent(peerNodeChildren, (const xmlChar *)tagContentHex);
								nodeUpdated = 1;
							}
johan's avatar
johan committed
292
						} else {
293
							peerNodeChildren = peerNodeChildren->next;
johan's avatar
johan committed
294 295 296 297 298 299
						}
					}
					if (nodeUpdated == 0) { /* if we didn't found our node, add it at the end of peer node */
						xmlNewTextChild(cur, NULL, (const xmlChar *)tagName, tagContentHex);
						nodeUpdated = 1;
					}
johan's avatar
johan committed
300 301
					xmlFree(currentZidHex);
					currentZidHex=NULL;
johan's avatar
johan committed
302 303
					break;
				}
304 305
				xmlFree(currentZidHex);
				currentZidHex=NULL;
johan's avatar
johan committed
306 307
			}
			cur = cur->next;
308 309
		}

johan's avatar
johan committed
310 311 312 313 314 315 316
		/* we didn't find the peer element, create it with nodes ZID and tagName */
		if (nodeUpdated == 0) {
			xmlNodePtr peerNode = xmlNewNode(NULL, (const xmlChar *)"peer");
			xmlNewTextChild(peerNode, NULL, (const xmlChar *)"ZID", peerZidHex);
			xmlNewTextChild(peerNode, NULL, (const xmlChar *)tagName, tagContentHex);
			xmlAddChild(rootNode, peerNode);
		}
317 318


johan's avatar
johan committed
319 320 321 322
		/* write the cache file if requested and unlock it(TODO)*/
		if ((fileFlag&BZRTP_CACHE_WRITEFILEBIT) == BZRTP_CACHE_WRITEFILE) {
			bzrtp_writeCache(context);
		}
johan's avatar
johan committed
323
	}
johan's avatar
johan committed
324

325 326
	free(tagContentHex);

johan's avatar
johan committed
327 328 329 330
	return 0;
}


johan's avatar
johan committed
331
/*** Local functions implementations ***/
johan's avatar
johan committed
332

333
/**
johan's avatar
johan committed
334
 * @brief write the cache from the xmlDoc to cache file
335 336 337 338
 *
 * @param[in/out]	zrtpContext		The zrtp context containing the cacheBuffer
 *
 */
339
static void bzrtp_writeCache(bzrtpContext_t *zrtpContext) {
johan's avatar
johan committed
340
	/* dump the xml document into a string */
341
	xmlChar *xmlStringOutput;
johan's avatar
johan committed
342 343 344
	int xmlStringLength;
	xmlDocDumpFormatMemoryEnc(zrtpContext->cacheBuffer, &xmlStringOutput, &xmlStringLength, "UTF-8", 0);
	/* write it to the file */
345
	zrtpContext->zrtpCallbacks.bzrtp_writeCache(zrtpContext->ZIDCacheData, xmlStringOutput, xmlStringLength);
346
	xmlFree(xmlStringOutput);
johan's avatar
johan committed
347
}
348 349 350 351 352 353 354

#else /* NOT HAVE_LIBXML2 */
int bzrtp_getSelfZID(bzrtpContext_t *context, uint8_t selfZID[12]) {
	if (context == NULL) {
		return ZRTP_ZIDCACHE_INVALID_CONTEXT; 
	}
	/* we are running cacheless, return a random number */
355
	bctoolbox_rng_get(context->RNGContext, selfZID, 12);
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379
	return 0; 
}
int bzrtp_getPeerAssociatedSecretsHash(bzrtpContext_t *context, uint8_t peerZID[12]) {
		if (context == NULL) {
		return ZRTP_ZIDCACHE_INVALID_CONTEXT;
	}

	/* resert cached secret buffer */
	free(context->cachedSecret.rs1);
	free(context->cachedSecret.rs2);
	free(context->cachedSecret.pbxsecret);
	free(context->cachedSecret.auxsecret);
	context->cachedSecret.rs1 = NULL;
	context->cachedSecret.rs1Length = 0;
	context->cachedSecret.rs2 = NULL;
	context->cachedSecret.rs2Length = 0;
	context->cachedSecret.pbxsecret = NULL;
	context->cachedSecret.pbxsecretLength = 0;
	context->cachedSecret.auxsecret = NULL;
	context->cachedSecret.auxsecretLength = 0;
	context->cachedSecret.previouslyVerifiedSas = 0;

	return 0;
}
johan's avatar
johan committed
380 381 382

/* just do nothing for the write peer node */
int bzrtp_writePeerNode(bzrtpContext_t *context, uint8_t peerZID[12], uint8_t *tagName, uint8_t tagNameLength, uint8_t *tagContent, uint32_t tagContentLength, uint8_t nodeFlag, uint8_t fileFlag) {
383 384 385 386
	return 0;
}

#endif /* HAVE LIBXML2 */