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