/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2010 Oracle and/or its affiliates. All rights reserved. * * Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common * Development and Distribution License("CDDL") (collectively, the * "License"). You may not use this file except in compliance with the * License. You can obtain a copy of the License at * http://www.netbeans.org/cddl-gplv2.html * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the * specific language governing permissions and limitations under the * License. When distributing the software, include this License Header * Notice in each file and include the License file at * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the GPL Version 2 section of the License file that * accompanied this code. If applicable, add the following below the * License Header, with the fields enclosed by brackets [] replaced by * your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * If you wish your version of this file to be governed by only the CDDL * or only the GPL Version 2, indicate your decision by adding * "[Contributor] elects to include this software in this distribution * under the [CDDL or GPL Version 2] license." If you do not indicate a * single choice of license, a recipient has the option to distribute * your version of this file under either the CDDL, the GPL Version 2 or * to extend the choice of license to its licensees as provided above. * However, if you add GPL Version 2 code and therefore, elected the GPL * Version 2 license, then the option applies only if the new code is * made subject to such option by the copyright holder. * * Contributor(s): * * Portions Copyrighted 2009 Sun Microsystems, Inc. */ package org.netbeans.modules.keyring.mac; import com.sun.jna.Pointer; import java.io.UnsupportedEncodingException; import java.util.logging.Level; import java.util.logging.Logger; import org.netbeans.spi.keyring.KeyringProvider; public class MacProvider implements KeyringProvider { private static final Logger LOG = Logger.getLogger(MacProvider.class.getName()); public boolean enabled() { return true; // test elsewhere if we are on a mac } public char[] read(String key) { try { byte[] serviceName = key.getBytes("UTF-8"); byte[] accountName = "JOSM".getBytes("UTF-8"); int[] dataLength = new int[1]; Pointer[] data = new Pointer[1]; error("find", SecurityLibrary.LIBRARY.SecKeychainFindGenericPassword(null, serviceName.length, serviceName, accountName.length, accountName, dataLength, data, null)); if (data[0] == null) { return null; } byte[] value = data[0].getByteArray(0, dataLength[0]); // XXX ought to call SecKeychainItemFreeContent return new String(value, "UTF-8").toCharArray(); } catch (UnsupportedEncodingException x) { LOG.log(Level.WARNING, null, x); return null; } } public void save(String key, char[] password, String description) { try { byte[] serviceName = key.getBytes("UTF-8"); byte[] accountName = "JOSM".getBytes("UTF-8"); // Keychain Access seems to expect UTF-8, so do not use Utils.chars2Bytes: byte[] data = new String(password).getBytes("UTF-8"); Pointer[] itemRef = new Pointer[1]; error("find (for save)", SecurityLibrary.LIBRARY.SecKeychainFindGenericPassword(null, serviceName.length, serviceName, accountName.length, accountName, null, null, itemRef)); if (itemRef[0] != null) { error("save (update)", SecurityLibrary.LIBRARY.SecKeychainItemModifyContent(itemRef[0], null, data.length, data)); SecurityLibrary.LIBRARY.CFRelease(itemRef[0]); } else { error("save (new)", SecurityLibrary.LIBRARY.SecKeychainAddGenericPassword(null, serviceName.length, serviceName, accountName.length, accountName, data.length, data, null)); } } catch (UnsupportedEncodingException x) { LOG.log(Level.WARNING, null, x); } // XXX use description somehow... better to use SecItemAdd with kSecAttrDescription } public void delete(String key) { try { byte[] serviceName = key.getBytes("UTF-8"); byte[] accountName = "JOSM".getBytes("UTF-8"); Pointer[] itemRef = new Pointer[1]; error("find (for delete)", SecurityLibrary.LIBRARY.SecKeychainFindGenericPassword(null, serviceName.length, serviceName, accountName.length, accountName, null, null, itemRef)); if (itemRef[0] != null) { error("delete", SecurityLibrary.LIBRARY.SecKeychainItemDelete(itemRef[0])); SecurityLibrary.LIBRARY.CFRelease(itemRef[0]); } } catch (UnsupportedEncodingException x) { LOG.log(Level.WARNING, null, x); } } private static void error(String msg, int code) { if (code != 0 && code != /* errSecItemNotFound, always returned from find it seems */-25300) { Pointer translated = SecurityLibrary.LIBRARY.SecCopyErrorMessageString(code, null); String str; if (translated == null) { str = String.valueOf(code); } else { char[] buf = new char[(int) SecurityLibrary.LIBRARY.CFStringGetLength(translated)]; for (int i = 0; i < buf.length; i++) { buf[i] = SecurityLibrary.LIBRARY.CFStringGetCharacterAtIndex(translated, i); } SecurityLibrary.LIBRARY.CFRelease(translated); str = new String(buf) + " (" + code + ")"; } LOG.log(Level.WARNING, "{0}: {1}", new Object[] {msg, str}); } } }