Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

[Security] Adds support for SecKeychainAdd(Generic|Internet)Password …

…and SecKeychainFind(Generic|Internet)Password

this greatly simplifies common operations with the Keychain under Mac OS X.
  • Loading branch information...
commit 9c54c332b93fdc4e252439a436a1f9b5359c788a 1 parent 7c731bd
LeszekW LeszekW authored migueldeicaza committed

Showing 1 changed file with 365 additions and 2 deletions. Show diff stats Hide diff stats

  1. +365 2 src/Security/Items.cs
367 src/Security/Items.cs
@@ -202,7 +202,370 @@ public static SecStatusCode Update (SecRecord query, SecRecord newAttributes)
202 202 return SecItem.SecItemUpdate (query.queryDict.Handle, newAttributes.queryDict.Handle);
203 203
204 204 }
205   -#if !MONOMAC
  205 +#if MONOMAC
  206 + [DllImport (Constants.SecurityLibrary)]
  207 + extern static SecStatusCode SecKeychainAddGenericPassword (
  208 + IntPtr keychain,
  209 + int serviceNameLength,
  210 + IntPtr serviceName,
  211 + int accountNameLength,
  212 + IntPtr accountName,
  213 + int passwordLength,
  214 + IntPtr passwordData,
  215 + IntPtr itemRef);
  216 +
  217 + [DllImport (Constants.SecurityLibrary)]
  218 + extern static SecStatusCode SecKeychainFindGenericPassword (
  219 + IntPtr keychainOrArray,
  220 + int serviceNameLength,
  221 + IntPtr serviceName,
  222 + int accountNameLength,
  223 + IntPtr accountName,
  224 + out int passwordLength,
  225 + out IntPtr passwordData,
  226 + IntPtr itemRef);
  227 +
  228 + [DllImport (Constants.SecurityLibrary)]
  229 + extern static SecStatusCode SecKeychainAddInternetPassword (
  230 + IntPtr keychain,
  231 + int serverNameLength,
  232 + IntPtr serverName,
  233 + int securityDomainLength,
  234 + IntPtr securityDomain,
  235 + int accountNameLength,
  236 + IntPtr accountName,
  237 + int pathLength,
  238 + IntPtr path,
  239 + short port,
  240 + IntPtr protocol,
  241 + IntPtr authenticationType,
  242 + int passwordLength,
  243 + IntPtr passwordData,
  244 + IntPtr itemRef);
  245 +
  246 + [DllImport (Constants.SecurityLibrary)]
  247 + extern static SecStatusCode SecKeychainFindInternetPassword (
  248 + IntPtr keychain,
  249 + int serverNameLength,
  250 + IntPtr serverName,
  251 + int securityDomainLength,
  252 + IntPtr securityDomain,
  253 + int accountNameLength,
  254 + IntPtr accountName,
  255 + int pathLength,
  256 + IntPtr path,
  257 + short port,
  258 + IntPtr protocol,
  259 + IntPtr authenticationType,
  260 + out int passwordLength,
  261 + out IntPtr passwordData,
  262 + IntPtr itemRef);
  263 +
  264 + [DllImport (Constants.SecurityLibrary)]
  265 + extern static SecStatusCode SecKeychainItemFreeContent (IntPtr attrList, IntPtr data);
  266 +
  267 + public static SecStatusCode AddInternetPassword (
  268 + string serverName,
  269 + string accountName,
  270 + byte[] password,
  271 + SecProtocol protocolType = SecProtocol.Http,
  272 + short port = 0,
  273 + string path = null,
  274 + SecAuthenticationType authenticationType = SecAuthenticationType.Default,
  275 + string securityDomain = null)
  276 + {
  277 + GCHandle serverHandle;
  278 + GCHandle securityDomainHandle;
  279 + GCHandle accountHandle;
  280 + GCHandle pathHandle;
  281 + GCHandle passwordHandle;
  282 +
  283 + int serverNameLength = 0;
  284 + IntPtr serverNamePtr = IntPtr.Zero;
  285 + int securityDomainLength = 0;
  286 + IntPtr securityDomainPtr = IntPtr.Zero;
  287 + int accountNameLength = 0;
  288 + IntPtr accountNamePtr = IntPtr.Zero;
  289 + int pathLength = 0;
  290 + IntPtr pathPtr = IntPtr.Zero;
  291 + int passwordLength = 0;
  292 + IntPtr passwordPtr = IntPtr.Zero;
  293 +
  294 + try {
  295 +
  296 + if (!String.IsNullOrEmpty (serverName)) {
  297 + var bytes = System.Text.Encoding.UTF8.GetBytes (serverName);
  298 + serverNameLength = bytes.Length;
  299 + serverHandle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
  300 + serverNamePtr = serverHandle.AddrOfPinnedObject ();
  301 + }
  302 +
  303 + if (!String.IsNullOrEmpty (securityDomain)) {
  304 + var bytes = System.Text.Encoding.UTF8.GetBytes (securityDomain);
  305 + securityDomainLength = bytes.Length;
  306 + securityDomainHandle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
  307 + }
  308 +
  309 + if (!String.IsNullOrEmpty (accountName)) {
  310 + var bytes = System.Text.Encoding.UTF8.GetBytes (accountName);
  311 + accountNameLength = bytes.Length;
  312 + accountHandle = GCHandle.Alloc (bytes, GCHandleType.Pinned);
  313 + accountNamePtr = accountHandle.AddrOfPinnedObject ();
  314 + }
  315 +
  316 + if (!String.IsNullOrEmpty(path)) {
  317 + var bytes = System.Text.Encoding.UTF8.GetBytes (path);
  318 + pathLength = bytes.Length;
  319 + pathHandle = GCHandle.Alloc (bytes, GCHandleType.Pinned);
  320 + pathPtr = pathHandle.AddrOfPinnedObject ();
  321 + }
  322 +
  323 + if (password != null && password.Length > 0) {
  324 + passwordLength = password.Length;
  325 + passwordHandle = GCHandle.Alloc (password, GCHandleType.Pinned);
  326 + passwordPtr = passwordHandle.AddrOfPinnedObject ();
  327 + }
  328 +
  329 + return SecKeychainAddInternetPassword (
  330 + IntPtr.Zero,
  331 + serverNameLength,
  332 + serverNamePtr,
  333 + securityDomainLength,
  334 + securityDomainPtr,
  335 + accountNameLength,
  336 + accountNamePtr,
  337 + pathLength,
  338 + pathPtr,
  339 + port,
  340 + SecProtocolKeys.FromSecProtocol (protocolType),
  341 + KeysAuthenticationType.FromSecAuthenticationType (authenticationType),
  342 + passwordLength,
  343 + passwordPtr,
  344 + IntPtr.Zero);
  345 + } finally {
  346 + if (serverHandle.IsAllocated)
  347 + serverHandle.Free ();
  348 + if (accountHandle.IsAllocated)
  349 + accountHandle.Free ();
  350 + if (passwordHandle.IsAllocated)
  351 + passwordHandle.Free ();
  352 + if (securityDomainHandle.IsAllocated)
  353 + securityDomainHandle.Free ();
  354 + if (pathHandle.IsAllocated)
  355 + pathHandle.Free ();
  356 + }
  357 + }
  358 +
  359 +
  360 + public static SecStatusCode FindInternetPassword(
  361 + string serverName,
  362 + string accountName,
  363 + out byte[] password,
  364 + SecProtocol protocolType = SecProtocol.Http,
  365 + short port = 0,
  366 + string path = null,
  367 + SecAuthenticationType authenticationType = SecAuthenticationType.Default,
  368 + string securityDomain = null)
  369 + {
  370 + password = null;
  371 +
  372 + GCHandle serverHandle;
  373 + GCHandle securityDomainHandle;
  374 + GCHandle accountHandle;
  375 + GCHandle pathHandle;
  376 +
  377 + int serverNameLength = 0;
  378 + IntPtr serverNamePtr = IntPtr.Zero;
  379 + int securityDomainLength = 0;
  380 + IntPtr securityDomainPtr = IntPtr.Zero;
  381 + int accountNameLength = 0;
  382 + IntPtr accountNamePtr = IntPtr.Zero;
  383 + int pathLength = 0;
  384 + IntPtr pathPtr = IntPtr.Zero;
  385 + IntPtr passwordPtr = IntPtr.Zero;
  386 +
  387 + try {
  388 + if (!String.IsNullOrEmpty(serverName)) {
  389 + var bytes = System.Text.Encoding.UTF8.GetBytes (serverName);
  390 + serverNameLength = bytes.Length;
  391 + serverHandle = GCHandle.Alloc (bytes, GCHandleType.Pinned);
  392 + serverNamePtr = serverHandle.AddrOfPinnedObject ();
  393 + }
  394 +
  395 + if (!String.IsNullOrEmpty(securityDomain)) {
  396 + var bytes = System.Text.Encoding.UTF8.GetBytes (securityDomain);
  397 + securityDomainLength = bytes.Length;
  398 + securityDomainHandle = GCHandle.Alloc (bytes, GCHandleType.Pinned);
  399 + }
  400 +
  401 + if (!String.IsNullOrEmpty(accountName)) {
  402 + var bytes = System.Text.Encoding.UTF8.GetBytes (accountName);
  403 + accountNameLength = bytes.Length;
  404 + accountHandle = GCHandle.Alloc (bytes, GCHandleType.Pinned);
  405 + accountNamePtr = accountHandle.AddrOfPinnedObject ();
  406 + }
  407 +
  408 + if (!String.IsNullOrEmpty(path)) {
  409 + var bytes = System.Text.Encoding.UTF8.GetBytes (path);
  410 + pathLength = bytes.Length;
  411 + pathHandle = GCHandle.Alloc (bytes, GCHandleType.Pinned);
  412 + pathPtr = pathHandle.AddrOfPinnedObject ();
  413 + }
  414 +
  415 + int passwordLength = 0;
  416 +
  417 + SecStatusCode code = SecKeychainFindInternetPassword(
  418 + IntPtr.Zero,
  419 + serverNameLength,
  420 + serverNamePtr,
  421 + securityDomainLength,
  422 + securityDomainPtr,
  423 + accountNameLength,
  424 + accountNamePtr,
  425 + pathLength,
  426 + pathPtr,
  427 + port,
  428 + SecProtocolKeys.FromSecProtocol(protocolType),
  429 + KeysAuthenticationType.FromSecAuthenticationType(authenticationType),
  430 + out passwordLength,
  431 + out passwordPtr,
  432 + IntPtr.Zero);
  433 +
  434 + if (code == SecStatusCode.Success && passwordLength > 0) {
  435 + password = new byte[passwordLength];
  436 + Marshal.Copy(passwordPtr, password, 0, passwordLength);
  437 + }
  438 +
  439 + return code;
  440 +
  441 + } finally {
  442 + if (serverHandle.IsAllocated)
  443 + serverHandle.Free();
  444 + if (accountHandle.IsAllocated)
  445 + accountHandle.Free();
  446 + if (securityDomainHandle.IsAllocated)
  447 + securityDomainHandle.Free();
  448 + if (pathHandle.IsAllocated)
  449 + pathHandle.Free();
  450 + if (passwordPtr != IntPtr.Zero)
  451 + SecKeychainItemFreeContent(IntPtr.Zero, passwordPtr);
  452 + }
  453 + }
  454 +
  455 + public static SecStatusCode AddGenericPassword (string serviceName, string accountName, byte[] password)
  456 + {
  457 + GCHandle serviceHandle;
  458 + GCHandle accountHandle;
  459 + GCHandle passwordHandle;
  460 +
  461 + int serviceNameLength = 0;
  462 + IntPtr serviceNamePtr = IntPtr.Zero;
  463 + int accountNameLength = 0;
  464 + IntPtr accountNamePtr = IntPtr.Zero;
  465 + int passwordLength = 0;
  466 + IntPtr passwordPtr = IntPtr.Zero;
  467 +
  468 + try {
  469 + if (!String.IsNullOrEmpty(serviceName)) {
  470 + var bytes = System.Text.Encoding.UTF8.GetBytes (serviceName);
  471 + serviceNameLength = bytes.Length;
  472 + serviceHandle = GCHandle.Alloc (bytes, GCHandleType.Pinned);
  473 + serviceNamePtr = serviceHandle.AddrOfPinnedObject ();
  474 + }
  475 +
  476 + if (!String.IsNullOrEmpty(accountName)) {
  477 + var bytes = System.Text.Encoding.UTF8.GetBytes (accountName);
  478 + accountNameLength = bytes.Length;
  479 + accountHandle = GCHandle.Alloc (bytes, GCHandleType.Pinned);
  480 + accountNamePtr = accountHandle.AddrOfPinnedObject ();
  481 + }
  482 +
  483 + if (password != null && password.Length > 0) {
  484 + passwordLength = password.Length;
  485 + passwordHandle = GCHandle.Alloc (password, GCHandleType.Pinned);
  486 + passwordPtr = passwordHandle.AddrOfPinnedObject ();
  487 + }
  488 +
  489 + return SecKeychainAddGenericPassword(
  490 + IntPtr.Zero,
  491 + serviceNameLength,
  492 + serviceNamePtr,
  493 + accountNameLength,
  494 + accountNamePtr,
  495 + passwordLength,
  496 + passwordPtr,
  497 + IntPtr.Zero
  498 + );
  499 +
  500 + } finally {
  501 + if (serviceHandle.IsAllocated)
  502 + serviceHandle.Free();
  503 + if (accountHandle.IsAllocated)
  504 + accountHandle.Free();
  505 + if (passwordHandle.IsAllocated)
  506 + passwordHandle.Free();
  507 + }
  508 + }
  509 +
  510 + public static SecStatusCode FindGenericPassword(string serviceName, string accountName, out byte[] password)
  511 + {
  512 + password = null;
  513 +
  514 + GCHandle serviceHandle;
  515 + GCHandle accountHandle;
  516 +
  517 + int serviceNameLength = 0;
  518 + IntPtr serviceNamePtr = IntPtr.Zero;
  519 + int accountNameLength = 0;
  520 + IntPtr accountNamePtr = IntPtr.Zero;
  521 + IntPtr passwordPtr = IntPtr.Zero;
  522 +
  523 + try {
  524 +
  525 + if (!String.IsNullOrEmpty(serviceName)) {
  526 + var bytes = System.Text.Encoding.UTF8.GetBytes (serviceName);
  527 + serviceNameLength = bytes.Length;
  528 + serviceHandle = GCHandle.Alloc (bytes, GCHandleType.Pinned);
  529 + serviceNamePtr = serviceHandle.AddrOfPinnedObject();
  530 + }
  531 +
  532 + if (!String.IsNullOrEmpty(accountName)) {
  533 + var bytes = System.Text.Encoding.UTF8.GetBytes (accountName);
  534 + accountNameLength = bytes.Length;
  535 + accountHandle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
  536 + accountNamePtr = accountHandle.AddrOfPinnedObject();
  537 + }
  538 +
  539 + int passwordLength = 0;
  540 +
  541 + var code = SecKeychainFindGenericPassword(
  542 + IntPtr.Zero,
  543 + serviceNameLength,
  544 + serviceNamePtr,
  545 + accountNameLength,
  546 + accountNamePtr,
  547 + out passwordLength,
  548 + out passwordPtr,
  549 + IntPtr.Zero
  550 + );
  551 +
  552 + if (code == SecStatusCode.Success && passwordLength > 0){
  553 + password = new byte[passwordLength];
  554 + Marshal.Copy(passwordPtr, password, 0, passwordLength);
  555 + }
  556 +
  557 + return code;
  558 +
  559 + } finally {
  560 + if (serviceHandle.IsAllocated)
  561 + serviceHandle.Free();
  562 + if (accountHandle.IsAllocated)
  563 + accountHandle.Free();
  564 + if (passwordPtr != IntPtr.Zero)
  565 + SecKeychainItemFreeContent(IntPtr.Zero, passwordPtr);
  566 + }
  567 + }
  568 +#else
206 569 public static object QueryAsConcreteType (SecRecord query, out SecStatusCode result)
207 570 {
208 571 if (query == null){
@@ -218,7 +581,7 @@ public static object QueryAsConcreteType (SecRecord query, out SecStatusCode res
218 581 result = SecItem.SecItemCopyMatching (copy.Handle, out ptr);
219 582 if (result == SecStatusCode.Success){
220 583 int cfType = CFType.GetTypeID (ptr);
221   -
  584 +
222 585 if (cfType == SecCertificate.GetTypeID ())
223 586 return new SecCertificate (ptr, true);
224 587 else if (cfType == SecKey.GetTypeID ())

0 comments on commit 9c54c33

Please sign in to comment.
Something went wrong with that request. Please try again.