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 10 Keyring::Keyring() 11 : 12 fHasUnlockKey(false), 13 fUnlocked(false), 14 fModified(false) 15 { 16 } 17 18 19 Keyring::Keyring(const char* name) 20 : 21 fName(name), 22 fHasUnlockKey(false), 23 fUnlocked(false), 24 fModified(false) 25 { 26 } 27 28 29 Keyring::~Keyring() 30 { 31 } 32 33 34 status_t 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 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 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 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 133 Keyring::IsUnlocked() const 134 { 135 return fUnlocked; 136 } 137 138 139 bool 140 Keyring::HasUnlockKey() const 141 { 142 return fHasUnlockKey; 143 } 144 145 146 const BMessage& 147 Keyring::UnlockKey() const 148 { 149 return fUnlockKey; 150 } 151 152 153 status_t 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 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 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 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 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 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 292 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 338 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 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 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 457 Keyring::Compare(const Keyring* one, const Keyring* two) 458 { 459 return strcmp(one->Name(), two->Name()); 460 } 461 462 463 int 464 Keyring::Compare(const BString* name, const Keyring* keyring) 465 { 466 return strcmp(name->String(), keyring->Name()); 467 } 468 469 470 status_t 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 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