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