xref: /haiku/src/servers/keystore/Keyring.cpp (revision 25a7b01d15612846f332751841da3579db313082)
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 
Keyring()10 Keyring::Keyring()
11 	:
12 	fHasUnlockKey(false),
13 	fUnlocked(false),
14 	fModified(false)
15 {
16 }
17 
18 
Keyring(const char * name)19 Keyring::Keyring(const char* name)
20 	:
21 	fName(name),
22 	fHasUnlockKey(false),
23 	fUnlocked(false),
24 	fModified(false)
25 {
26 }
27 
28 
~Keyring()29 Keyring::~Keyring()
30 {
31 }
32 
33 
34 status_t
ReadFromMessage(const BMessage & message)35 Keyring::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 
70 status_t
WriteToMessage(BMessage & message)71 Keyring::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 
94 status_t
Unlock(const BMessage * keyMessage)95 Keyring::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 
117 void
Lock()118 Keyring::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 
132 bool
IsUnlocked() const133 Keyring::IsUnlocked() const
134 {
135 	return fUnlocked;
136 }
137 
138 
139 bool
HasUnlockKey() const140 Keyring::HasUnlockKey() const
141 {
142 	return fHasUnlockKey;
143 }
144 
145 
146 const BMessage&
UnlockKey() const147 Keyring::UnlockKey() const
148 {
149 	return fUnlockKey;
150 }
151 
152 
153 status_t
SetUnlockKey(const BMessage & keyMessage)154 Keyring::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 
166 status_t
RemoveUnlockKey()167 Keyring::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 
179 status_t
GetNextApplication(uint32 & cookie,BString & signature,BString & path)180 Keyring::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 
206 status_t
FindApplication(const char * signature,const char * path,BMessage & appMessage)207 Keyring::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 
235 status_t
AddApplication(const char * signature,const BMessage & appMessage)236 Keyring::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 
250 status_t
RemoveApplication(const char * signature,const char * path)251 Keyring::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 
291 status_t
FindKey(const BString & identifier,const BString & secondaryIdentifier,bool secondaryIdentifierOptional,BMessage * _foundKeyMessage) const292 Keyring::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 
337 status_t
FindKey(BKeyType type,BKeyPurpose purpose,uint32 index,BMessage & _foundKeyMessage) const338 Keyring::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 
402 status_t
AddKey(const BString & identifier,const BString & secondaryIdentifier,const BMessage & keyMessage)403 Keyring::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 
423 status_t
RemoveKey(const BString & identifier,const BMessage & keyMessage)424 Keyring::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 
456 int
Compare(const Keyring * one,const Keyring * two)457 Keyring::Compare(const Keyring* one, const Keyring* two)
458 {
459 	return strcmp(one->Name(), two->Name());
460 }
461 
462 
463 int
Compare(const BString * name,const Keyring * keyring)464 Keyring::Compare(const BString* name, const Keyring* keyring)
465 {
466 	return strcmp(name->String(), keyring->Name());
467 }
468 
469 
470 status_t
_EncryptToFlatBuffer()471 Keyring::_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 
504 status_t
_DecryptFromFlatBuffer()505 Keyring::_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