1/*
2 * Copyright 2012, Michael Lotz, mmlr@mlotz.ch. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include "Keyring.h"
8
9
10Keyring::Keyring()
11	:
12	fHasUnlockKey(false),
13	fUnlocked(false),
14	fModified(false)
15{
16}
17
18
19Keyring::Keyring(const char* name)
20	:
21	fName(name),
22	fHasUnlockKey(false),
23	fUnlocked(false),
24	fModified(false)
25{
26}
27
28
29Keyring::~Keyring()
30{
31}
32
33
34status_t
35Keyring::ReadFromMessage(const BMessage& message)
36{
37	status_t result = message.FindString("name", &fName);
38	if (result != B_OK)
39		return result;
40
41	result = message.FindBool("hasUnlockKey", &fHasUnlockKey);
42	if (result != B_OK)
43		return result;
44
45	if (message.GetBool("noData", false)) {
46		fFlatBuffer.SetSize(0);
47		return B_OK;
48	}
49
50	ssize_t size;
51	const void* data;
52	result = message.FindData("data", B_RAW_TYPE, &data, &size);
53	if (result != B_OK)
54		return result;
55
56	if (size < 0)
57		return B_ERROR;
58
59	fFlatBuffer.SetSize(0);
60	ssize_t written = fFlatBuffer.WriteAt(0, data, size);
61	if (written != size) {
62		fFlatBuffer.SetSize(0);
63		return written < 0 ? written : B_ERROR;
64	}
65
66	return B_OK;
67}
68
69
70status_t
71Keyring::WriteToMessage(BMessage& message)
72{
73	status_t result = _EncryptToFlatBuffer();
74	if (result != B_OK)
75		return result;
76
77	if (fFlatBuffer.BufferLength() == 0)
78		result = message.AddBool("noData", true);
79	else {
80		result = message.AddData("data", B_RAW_TYPE, fFlatBuffer.Buffer(),
81			fFlatBuffer.BufferLength());
82	}
83	if (result != B_OK)
84		return result;
85
86	result = message.AddBool("hasUnlockKey", fHasUnlockKey);
87	if (result != B_OK)
88		return result;
89
90	return message.AddString("name", fName);
91}
92
93
94status_t
95Keyring::Unlock(const BMessage* keyMessage)
96{
97	if (fUnlocked)
98		return B_OK;
99
100	if (fHasUnlockKey == (keyMessage == NULL))
101		return B_BAD_VALUE;
102
103	if (keyMessage != NULL)
104		fUnlockKey = *keyMessage;
105
106	status_t result = _DecryptFromFlatBuffer();
107	if (result != B_OK) {
108		fUnlockKey.MakeEmpty();
109		return result;
110	}
111
112	fUnlocked = true;
113	return B_OK;
114}
115
116
117void
118Keyring::Lock()
119{
120	if (!fUnlocked)
121		return;
122
123	_EncryptToFlatBuffer();
124
125	fUnlockKey.MakeEmpty();
126	fData.MakeEmpty();
127	fApplications.MakeEmpty();
128	fUnlocked = false;
129}
130
131
132bool
133Keyring::IsUnlocked() const
134{
135	return fUnlocked;
136}
137
138
139bool
140Keyring::HasUnlockKey() const
141{
142	return fHasUnlockKey;
143}
144
145
146const BMessage&
147Keyring::UnlockKey() const
148{
149	return fUnlockKey;
150}
151
152
153status_t
154Keyring::SetUnlockKey(const BMessage& keyMessage)
155{
156	if (!fUnlocked)
157		return B_NOT_ALLOWED;
158
159	fHasUnlockKey = true;
160	fUnlockKey = keyMessage;
161	fModified = true;
162	return B_OK;
163}
164
165
166status_t
167Keyring::RemoveUnlockKey()
168{
169	if (!fUnlocked)
170		return B_NOT_ALLOWED;
171
172	fUnlockKey.MakeEmpty();
173	fHasUnlockKey = false;
174	fModified = true;
175	return B_OK;
176}
177
178
179status_t
180Keyring::GetNextApplication(uint32& cookie, BString& signature,
181	BString& path)
182{
183	if (!fUnlocked)
184		return B_NOT_ALLOWED;
185
186	char* nameFound = NULL;
187	status_t result = fApplications.GetInfo(B_MESSAGE_TYPE, cookie++,
188		&nameFound, NULL);
189	if (result != B_OK)
190		return B_ENTRY_NOT_FOUND;
191
192	BMessage appMessage;
193	result = fApplications.FindMessage(nameFound, &appMessage);
194	if (result != B_OK)
195		return B_ENTRY_NOT_FOUND;
196
197	result = appMessage.FindString("path", &path);
198	if (result != B_OK)
199		return B_ERROR;
200
201	signature = nameFound;
202	return B_OK;
203}
204
205
206status_t
207Keyring::FindApplication(const char* signature, const char* path,
208	BMessage& appMessage)
209{
210	if (!fUnlocked)
211		return B_NOT_ALLOWED;
212
213	int32 count;
214	type_code type;
215	if (fApplications.GetInfo(signature, &type, &count) != B_OK)
216		return B_ENTRY_NOT_FOUND;
217
218	for (int32 i = 0; i < count; i++) {
219		if (fApplications.FindMessage(signature, i, &appMessage) != B_OK)
220			continue;
221
222		BString appPath;
223		if (appMessage.FindString("path", &appPath) != B_OK)
224			continue;
225
226		if (appPath == path)
227			return B_OK;
228	}
229
230	appMessage.MakeEmpty();
231	return B_ENTRY_NOT_FOUND;
232}
233
234
235status_t
236Keyring::AddApplication(const char* signature, const BMessage& appMessage)
237{
238	if (!fUnlocked)
239		return B_NOT_ALLOWED;
240
241	status_t result = fApplications.AddMessage(signature, &appMessage);
242	if (result != B_OK)
243		return result;
244
245	fModified = true;
246	return B_OK;
247}
248
249
250status_t
251Keyring::RemoveApplication(const char* signature, const char* path)
252{
253	if (!fUnlocked)
254		return B_NOT_ALLOWED;
255
256	if (path == NULL) {
257		// We want all of the entries for this signature removed.
258		status_t result = fApplications.RemoveName(signature);
259		if (result != B_OK)
260			return B_ENTRY_NOT_FOUND;
261
262		fModified = true;
263		return B_OK;
264	}
265
266	int32 count;
267	type_code type;
268	if (fApplications.GetInfo(signature, &type, &count) != B_OK)
269		return B_ENTRY_NOT_FOUND;
270
271	for (int32 i = 0; i < count; i++) {
272		BMessage appMessage;
273		if (fApplications.FindMessage(signature, i, &appMessage) != B_OK)
274			return B_ERROR;
275
276		BString appPath;
277		if (appMessage.FindString("path", &appPath) != B_OK)
278			continue;
279
280		if (appPath == path) {
281			fApplications.RemoveData(signature, i);
282			fModified = true;
283			return B_OK;
284		}
285	}
286
287	return B_ENTRY_NOT_FOUND;
288}
289
290
291status_t
292Keyring::FindKey(const BString& identifier, const BString& secondaryIdentifier,
293	bool secondaryIdentifierOptional, BMessage* _foundKeyMessage) const
294{
295	if (!fUnlocked)
296		return B_NOT_ALLOWED;
297
298	int32 count;
299	type_code type;
300	if (fData.GetInfo(identifier, &type, &count) != B_OK)
301		return B_ENTRY_NOT_FOUND;
302
303	// We have a matching primary identifier, need to check for the secondary
304	// identifier.
305	for (int32 i = 0; i < count; i++) {
306		BMessage candidate;
307		if (fData.FindMessage(identifier, i, &candidate) != B_OK)
308			return B_ERROR;
309
310		BString candidateIdentifier;
311		if (candidate.FindString("secondaryIdentifier",
312				&candidateIdentifier) != B_OK) {
313			candidateIdentifier = "";
314		}
315
316		if (candidateIdentifier == secondaryIdentifier) {
317			if (_foundKeyMessage != NULL)
318				*_foundKeyMessage = candidate;
319			return B_OK;
320		}
321	}
322
323	// We didn't find an exact match.
324	if (secondaryIdentifierOptional) {
325		if (_foundKeyMessage == NULL)
326			return B_OK;
327
328		// The secondary identifier is optional, so we just return the
329		// first entry.
330		return fData.FindMessage(identifier, 0, _foundKeyMessage);
331	}
332
333	return B_ENTRY_NOT_FOUND;
334}
335
336
337status_t
338Keyring::FindKey(BKeyType type, BKeyPurpose purpose, uint32 index,
339	BMessage& _foundKeyMessage) const
340{
341	if (!fUnlocked)
342		return B_NOT_ALLOWED;
343
344	for (int32 keyIndex = 0;; keyIndex++) {
345		int32 count = 0;
346		char* identifier = NULL;
347		if (fData.GetInfo(B_MESSAGE_TYPE, keyIndex, &identifier, NULL,
348				&count) != B_OK) {
349			break;
350		}
351
352		if (type == B_KEY_TYPE_ANY && purpose == B_KEY_PURPOSE_ANY) {
353			// No need to inspect the actual keys.
354			if ((int32)index >= count) {
355				index -= count;
356				continue;
357			}
358
359			return fData.FindMessage(identifier, index, &_foundKeyMessage);
360		}
361
362		// Go through the keys to check their type and purpose.
363		for (int32 subkeyIndex = 0; subkeyIndex < count; subkeyIndex++) {
364			BMessage subkey;
365			if (fData.FindMessage(identifier, subkeyIndex, &subkey) != B_OK)
366				return B_ERROR;
367
368			bool match = true;
369			if (type != B_KEY_TYPE_ANY) {
370				BKeyType subkeyType;
371				if (subkey.FindUInt32("type", (uint32*)&subkeyType) != B_OK)
372					return B_ERROR;
373
374				match = subkeyType == type;
375			}
376
377			if (match && purpose != B_KEY_PURPOSE_ANY) {
378				BKeyPurpose subkeyPurpose;
379				if (subkey.FindUInt32("purpose", (uint32*)&subkeyPurpose)
380						!= B_OK) {
381					return B_ERROR;
382				}
383
384				match = subkeyPurpose == purpose;
385			}
386
387			if (match) {
388				if (index == 0) {
389					_foundKeyMessage = subkey;
390					return B_OK;
391				}
392
393				index--;
394			}
395		}
396	}
397
398	return B_ENTRY_NOT_FOUND;
399}
400
401
402status_t
403Keyring::AddKey(const BString& identifier, const BString& secondaryIdentifier,
404	const BMessage& keyMessage)
405{
406	if (!fUnlocked)
407		return B_NOT_ALLOWED;
408
409	// Check for collisions.
410	if (FindKey(identifier, secondaryIdentifier, false, NULL) == B_OK)
411		return B_NAME_IN_USE;
412
413	// We're fine, just add the new key.
414	status_t result = fData.AddMessage(identifier, &keyMessage);
415	if (result != B_OK)
416		return result;
417
418	fModified = true;
419	return B_OK;
420}
421
422
423status_t
424Keyring::RemoveKey(const BString& identifier,
425	const BMessage& keyMessage)
426{
427	if (!fUnlocked)
428		return B_NOT_ALLOWED;
429
430	int32 count;
431	type_code type;
432	if (fData.GetInfo(identifier, &type, &count) != B_OK)
433		return B_ENTRY_NOT_FOUND;
434
435	for (int32 i = 0; i < count; i++) {
436		BMessage candidate;
437		if (fData.FindMessage(identifier, i, &candidate) != B_OK)
438			return B_ERROR;
439
440		// We require an exact match.
441		if (!candidate.HasSameData(keyMessage))
442			continue;
443
444		status_t result = fData.RemoveData(identifier, i);
445		if (result != B_OK)
446			return result;
447
448		fModified = true;
449		return B_OK;
450	}
451
452	return B_ENTRY_NOT_FOUND;
453}
454
455
456int
457Keyring::Compare(const Keyring* one, const Keyring* two)
458{
459	return strcmp(one->Name(), two->Name());
460}
461
462
463int
464Keyring::Compare(const BString* name, const Keyring* keyring)
465{
466	return strcmp(name->String(), keyring->Name());
467}
468
469
470status_t
471Keyring::_EncryptToFlatBuffer()
472{
473	if (!fModified)
474		return B_OK;
475
476	if (!fUnlocked)
477		return B_NOT_ALLOWED;
478
479	BMessage container;
480	status_t result = container.AddMessage("data", &fData);
481	if (result != B_OK)
482		return result;
483
484	result = container.AddMessage("applications", &fApplications);
485	if (result != B_OK)
486		return result;
487
488	fFlatBuffer.SetSize(0);
489	fFlatBuffer.Seek(0, SEEK_SET);
490
491	result = container.Flatten(&fFlatBuffer);
492	if (result != B_OK)
493		return result;
494
495	if (fHasUnlockKey) {
496		// TODO: Actually encrypt the flat buffer...
497	}
498
499	fModified = false;
500	return B_OK;
501}
502
503
504status_t
505Keyring::_DecryptFromFlatBuffer()
506{
507	if (fFlatBuffer.BufferLength() == 0)
508		return B_OK;
509
510	if (fHasUnlockKey) {
511		// TODO: Actually decrypt the flat buffer...
512	}
513
514	BMessage container;
515	fFlatBuffer.Seek(0, SEEK_SET);
516	status_t result = container.Unflatten(&fFlatBuffer);
517	if (result != B_OK)
518		return result;
519
520	result = container.FindMessage("data", &fData);
521	if (result != B_OK)
522		return result;
523
524	result = container.FindMessage("applications", &fApplications);
525	if (result != B_OK) {
526		fData.MakeEmpty();
527		return result;
528	}
529
530	return B_OK;
531}
532