nsPK11TokenDB.cpp (8641B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 #include "nsPK11TokenDB.h" 7 8 #include <string.h> 9 10 #include "ScopedNSSTypes.h" 11 #include "mozilla/Casting.h" 12 #include "mozilla/Logging.h" 13 #include "nsISupports.h" 14 #include "nsNSSCertHelper.h" 15 #include "nsNSSComponent.h" 16 #include "nsPromiseFlatString.h" 17 #include "nsReadableUtils.h" 18 #include "nsServiceManagerUtils.h" 19 #include "prerror.h" 20 #include "secerr.h" 21 22 extern mozilla::LazyLogModule gPIPNSSLog; 23 24 NS_IMPL_ISUPPORTS(nsPK11Token, nsIPK11Token) 25 26 nsPK11Token::nsPK11Token(PK11SlotInfo* slot) : mUIContext(new PipUIContext()) { 27 MOZ_ASSERT(slot); 28 mSlot.reset(PK11_ReferenceSlot(slot)); 29 mIsInternalCryptoToken = 30 PK11_IsInternal(mSlot.get()) && !PK11_IsInternalKeySlot(mSlot.get()); 31 mIsInternalKeyToken = PK11_IsInternalKeySlot(mSlot.get()); 32 mSeries = PK11_GetSlotSeries(slot); 33 (void)refreshTokenInfo(); 34 } 35 36 nsresult nsPK11Token::refreshTokenInfo() { 37 if (mIsInternalCryptoToken) { 38 nsresult rv; 39 if (PK11_IsFIPS()) { 40 rv = GetPIPNSSBundleString("Fips140TokenDescription", mTokenName); 41 } else { 42 rv = GetPIPNSSBundleString("TokenDescription", mTokenName); 43 } 44 if (NS_FAILED(rv)) { 45 return rv; 46 } 47 } else if (mIsInternalKeyToken) { 48 nsresult rv = GetPIPNSSBundleString("PrivateTokenDescription", mTokenName); 49 if (NS_FAILED(rv)) { 50 return rv; 51 } 52 } else { 53 mTokenName.Assign(PK11_GetTokenName(mSlot.get())); 54 } 55 56 CK_TOKEN_INFO tokInfo; 57 nsresult rv = mozilla::MapSECStatus(PK11_GetTokenInfo(mSlot.get(), &tokInfo)); 58 if (NS_FAILED(rv)) { 59 return rv; 60 } 61 62 // Set the Manufacturer field 63 if (mIsInternalCryptoToken || mIsInternalKeyToken) { 64 rv = GetPIPNSSBundleString("ManufacturerID", mTokenManufacturerID); 65 if (NS_FAILED(rv)) { 66 return rv; 67 } 68 } else { 69 const char* ccManID = 70 mozilla::BitwiseCast<char*, CK_UTF8CHAR*>(tokInfo.manufacturerID); 71 mTokenManufacturerID.Assign( 72 ccManID, strnlen(ccManID, sizeof(tokInfo.manufacturerID))); 73 mTokenManufacturerID.Trim(" ", false, true); 74 } 75 76 // Set the Hardware Version field 77 mTokenHWVersion.Truncate(); 78 mTokenHWVersion.AppendInt(tokInfo.hardwareVersion.major); 79 mTokenHWVersion.Append('.'); 80 mTokenHWVersion.AppendInt(tokInfo.hardwareVersion.minor); 81 82 // Set the Firmware Version field 83 mTokenFWVersion.Truncate(); 84 mTokenFWVersion.AppendInt(tokInfo.firmwareVersion.major); 85 mTokenFWVersion.Append('.'); 86 mTokenFWVersion.AppendInt(tokInfo.firmwareVersion.minor); 87 88 // Set the Serial Number field 89 const char* ccSerial = 90 mozilla::BitwiseCast<char*, CK_CHAR*>(tokInfo.serialNumber); 91 mTokenSerialNum.Assign(ccSerial, 92 strnlen(ccSerial, sizeof(tokInfo.serialNumber))); 93 mTokenSerialNum.Trim(" ", false, true); 94 95 return NS_OK; 96 } 97 98 nsresult nsPK11Token::GetAttributeHelper(const nsACString& attribute, 99 /*out*/ nsACString& xpcomOutParam) { 100 // Handle removals/insertions. 101 if (PK11_GetSlotSeries(mSlot.get()) != mSeries) { 102 nsresult rv = refreshTokenInfo(); 103 if (NS_FAILED(rv)) { 104 return rv; 105 } 106 } 107 108 xpcomOutParam = attribute; 109 return NS_OK; 110 } 111 112 NS_IMETHODIMP 113 nsPK11Token::GetTokenName(/*out*/ nsACString& tokenName) { 114 return GetAttributeHelper(mTokenName, tokenName); 115 } 116 117 NS_IMETHODIMP 118 nsPK11Token::GetIsInternalKeyToken(/*out*/ bool* _retval) { 119 NS_ENSURE_ARG_POINTER(_retval); 120 *_retval = mIsInternalKeyToken; 121 return NS_OK; 122 } 123 124 NS_IMETHODIMP 125 nsPK11Token::GetTokenManID(/*out*/ nsACString& tokenManufacturerID) { 126 return GetAttributeHelper(mTokenManufacturerID, tokenManufacturerID); 127 } 128 129 NS_IMETHODIMP 130 nsPK11Token::GetTokenHWVersion(/*out*/ nsACString& tokenHWVersion) { 131 return GetAttributeHelper(mTokenHWVersion, tokenHWVersion); 132 } 133 134 NS_IMETHODIMP 135 nsPK11Token::GetTokenFWVersion(/*out*/ nsACString& tokenFWVersion) { 136 return GetAttributeHelper(mTokenFWVersion, tokenFWVersion); 137 } 138 139 NS_IMETHODIMP 140 nsPK11Token::GetTokenSerialNumber(/*out*/ nsACString& tokenSerialNum) { 141 return GetAttributeHelper(mTokenSerialNum, tokenSerialNum); 142 } 143 144 NS_IMETHODIMP 145 nsPK11Token::IsLoggedIn(bool* _retval) { 146 NS_ENSURE_ARG_POINTER(_retval); 147 *_retval = PK11_IsLoggedIn(mSlot.get(), 0); 148 return NS_OK; 149 } 150 151 NS_IMETHODIMP 152 nsPK11Token::Login(bool force) { 153 nsresult rv; 154 bool test; 155 rv = this->NeedsLogin(&test); 156 if (NS_FAILED(rv)) return rv; 157 if (test && force) { 158 rv = this->LogoutSimple(); 159 if (NS_FAILED(rv)) return rv; 160 } 161 rv = setPassword(mSlot.get(), mUIContext); 162 if (NS_FAILED(rv)) return rv; 163 164 return mozilla::MapSECStatus( 165 PK11_Authenticate(mSlot.get(), true, mUIContext)); 166 } 167 168 NS_IMETHODIMP 169 nsPK11Token::LogoutSimple() { 170 // PK11_Logout() can fail if the user wasn't logged in beforehand. We want 171 // this method to succeed even in this case, so we ignore the return value. 172 (void)PK11_Logout(mSlot.get()); 173 return NS_OK; 174 } 175 176 NS_IMETHODIMP 177 nsPK11Token::LogoutAndDropAuthenticatedResources() { 178 static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID); 179 180 nsresult rv = LogoutSimple(); 181 182 if (NS_FAILED(rv)) return rv; 183 184 nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(kNSSComponentCID, &rv)); 185 if (NS_FAILED(rv)) return rv; 186 187 return nssComponent->LogoutAuthenticatedPK11(); 188 } 189 190 NS_IMETHODIMP 191 nsPK11Token::Reset() { 192 return mozilla::MapSECStatus(PK11_ResetToken(mSlot.get(), nullptr)); 193 } 194 195 NS_IMETHODIMP 196 nsPK11Token::GetNeedsUserInit(bool* aNeedsUserInit) { 197 NS_ENSURE_ARG_POINTER(aNeedsUserInit); 198 *aNeedsUserInit = PK11_NeedUserInit(mSlot.get()); 199 return NS_OK; 200 } 201 202 NS_IMETHODIMP 203 nsPK11Token::CheckPassword(const nsACString& password, bool* _retval) { 204 NS_ENSURE_ARG_POINTER(_retval); 205 SECStatus srv = 206 PK11_CheckUserPassword(mSlot.get(), PromiseFlatCString(password).get()); 207 if (srv != SECSuccess) { 208 *_retval = false; 209 PRErrorCode error = PR_GetError(); 210 if (error != SEC_ERROR_BAD_PASSWORD) { 211 /* something really bad happened - throw an exception */ 212 return mozilla::psm::GetXPCOMFromNSSError(error); 213 } 214 } else { 215 *_retval = true; 216 } 217 return NS_OK; 218 } 219 220 NS_IMETHODIMP 221 nsPK11Token::InitPassword(const nsACString& initialPassword) { 222 const nsCString& passwordCStr = PromiseFlatCString(initialPassword); 223 // PSM initializes the sqlite-backed softoken with an empty password. The 224 // implementation considers this not to be a password (GetHasPassword returns 225 // false), but we can't actually call PK11_InitPin again. Instead, we call 226 // PK11_ChangePW with the empty password. 227 bool hasPassword; 228 nsresult rv = GetHasPassword(&hasPassword); 229 if (NS_FAILED(rv)) { 230 return rv; 231 } 232 if (!PK11_NeedUserInit(mSlot.get()) && !hasPassword) { 233 return mozilla::MapSECStatus( 234 PK11_ChangePW(mSlot.get(), "", passwordCStr.get())); 235 } 236 return mozilla::MapSECStatus( 237 PK11_InitPin(mSlot.get(), "", passwordCStr.get())); 238 } 239 240 NS_IMETHODIMP 241 nsPK11Token::ChangePassword(const nsACString& oldPassword, 242 const nsACString& newPassword) { 243 // PK11_ChangePW() has different semantics for the empty string and for 244 // nullptr. In order to support this difference, we need to check IsVoid() to 245 // find out if our caller supplied null/undefined args or just empty strings. 246 // See Bug 447589. 247 return mozilla::MapSECStatus(PK11_ChangePW( 248 mSlot.get(), 249 oldPassword.IsVoid() ? nullptr : PromiseFlatCString(oldPassword).get(), 250 newPassword.IsVoid() ? nullptr : PromiseFlatCString(newPassword).get())); 251 } 252 253 NS_IMETHODIMP 254 nsPK11Token::GetHasPassword(bool* hasPassword) { 255 NS_ENSURE_ARG_POINTER(hasPassword); 256 // PK11_NeedLogin returns true if the token is currently configured to require 257 // the user to log in (whether or not the user is actually logged in makes no 258 // difference). 259 *hasPassword = PK11_NeedLogin(mSlot.get()) && !PK11_NeedUserInit(mSlot.get()); 260 return NS_OK; 261 } 262 263 NS_IMETHODIMP 264 nsPK11Token::NeedsLogin(bool* _retval) { 265 NS_ENSURE_ARG_POINTER(_retval); 266 *_retval = PK11_NeedLogin(mSlot.get()); 267 return NS_OK; 268 } 269 270 /*=========================================================*/ 271 272 NS_IMPL_ISUPPORTS(nsPK11TokenDB, nsIPK11TokenDB) 273 274 NS_IMETHODIMP 275 nsPK11TokenDB::GetInternalKeyToken(nsIPK11Token** _retval) { 276 NS_ENSURE_ARG_POINTER(_retval); 277 mozilla::UniquePK11SlotInfo slot(PK11_GetInternalKeySlot()); 278 if (!slot) { 279 return NS_ERROR_FAILURE; 280 } 281 282 nsCOMPtr<nsIPK11Token> token = new nsPK11Token(slot.get()); 283 token.forget(_retval); 284 285 return NS_OK; 286 }