zidCache.c 16.6 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 30
#include "zidCache.h"

31
#include "typedef.h"
32
#include "cryptoUtils.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 56
		zrtpFreeBuffer_callback cb=NULL;
		context->zrtpCallbacks.bzrtp_loadCache(context->channelContext[0]->clientData, &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 */
johan's avatar
johan committed
61
		bzrtpCrypto_getRandom(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 90 91 92 93
		/* generate a random ZID */
		bzrtpCrypto_getRandom(context->RNGContext, selfZID, 12);
		/* 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;
Simon Morlat's avatar
Simon Morlat committed
251
		context->zrtpCallbacks.bzrtp_loadCache(context->channelContext[0]->clientData, &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 345
	int xmlStringLength;
	xmlDocDumpFormatMemoryEnc(zrtpContext->cacheBuffer, &xmlStringOutput, &xmlStringLength, "UTF-8", 0);
	/* write it to the file */
	zrtpContext->zrtpCallbacks.bzrtp_writeCache(zrtpContext->channelContext[0]->clientData, xmlStringOutput, xmlStringLength);
346
	xmlFree(xmlStringOutput);
johan's avatar
johan committed
347
}
348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379

#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 */
	bzrtpCrypto_getRandom(context->RNGContext, selfZID, 12);
	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 */