|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 |
|
6 const Cc = Components.classes; |
|
7 const Ci = Components.interfaces; |
|
8 const Cr = Components.results; |
|
9 |
|
10 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); |
|
11 Components.utils.import("resource://gre/modules/Services.jsm"); |
|
12 |
|
13 function LoginManagerCrypto_SDR() { |
|
14 this.init(); |
|
15 }; |
|
16 |
|
17 LoginManagerCrypto_SDR.prototype = { |
|
18 |
|
19 classID : Components.ID("{dc6c2976-0f73-4f1f-b9ff-3d72b4e28309}"), |
|
20 QueryInterface : XPCOMUtils.generateQI([Ci.nsILoginManagerCrypto]), |
|
21 |
|
22 __sdrSlot : null, // PKCS#11 slot being used by the SDR. |
|
23 get _sdrSlot() { |
|
24 if (!this.__sdrSlot) { |
|
25 let modules = Cc["@mozilla.org/security/pkcs11moduledb;1"]. |
|
26 getService(Ci.nsIPKCS11ModuleDB); |
|
27 this.__sdrSlot = modules.findSlotByName(""); |
|
28 } |
|
29 return this.__sdrSlot; |
|
30 }, |
|
31 |
|
32 __decoderRing : null, // nsSecretDecoderRing service |
|
33 get _decoderRing() { |
|
34 if (!this.__decoderRing) |
|
35 this.__decoderRing = Cc["@mozilla.org/security/sdr;1"]. |
|
36 getService(Ci.nsISecretDecoderRing); |
|
37 return this.__decoderRing; |
|
38 }, |
|
39 |
|
40 __utfConverter : null, // UCS2 <--> UTF8 string conversion |
|
41 get _utfConverter() { |
|
42 if (!this.__utfConverter) { |
|
43 this.__utfConverter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]. |
|
44 createInstance(Ci.nsIScriptableUnicodeConverter); |
|
45 this.__utfConverter.charset = "UTF-8"; |
|
46 } |
|
47 return this.__utfConverter; |
|
48 }, |
|
49 |
|
50 _utfConverterReset : function() { |
|
51 this.__utfConverter = null; |
|
52 }, |
|
53 |
|
54 _debug : false, // mirrors signon.debug |
|
55 _uiBusy : false, |
|
56 |
|
57 |
|
58 /* |
|
59 * log |
|
60 * |
|
61 * Internal function for logging debug messages to the Error Console. |
|
62 */ |
|
63 log : function (message) { |
|
64 if (!this._debug) |
|
65 return; |
|
66 dump("PwMgr cryptoSDR: " + message + "\n"); |
|
67 Services.console.logStringMessage("PwMgr cryptoSDR: " + message); |
|
68 }, |
|
69 |
|
70 |
|
71 init : function () { |
|
72 // Connect to the correct preferences branch. |
|
73 this._prefBranch = Services.prefs.getBranch("signon."); |
|
74 |
|
75 this._debug = this._prefBranch.getBoolPref("debug"); |
|
76 |
|
77 // Check to see if the internal PKCS#11 token has been initialized. |
|
78 // If not, set a blank password. |
|
79 let tokenDB = Cc["@mozilla.org/security/pk11tokendb;1"]. |
|
80 getService(Ci.nsIPK11TokenDB); |
|
81 |
|
82 let token = tokenDB.getInternalKeyToken(); |
|
83 if (token.needsUserInit) { |
|
84 this.log("Initializing key3.db with default blank password."); |
|
85 token.initPassword(""); |
|
86 } |
|
87 }, |
|
88 |
|
89 |
|
90 /* |
|
91 * encrypt |
|
92 * |
|
93 * Encrypts the specified string, using the SecretDecoderRing. |
|
94 * |
|
95 * Returns the encrypted string, or throws an exception if there was a |
|
96 * problem. |
|
97 */ |
|
98 encrypt : function (plainText) { |
|
99 let cipherText = null; |
|
100 |
|
101 let wasLoggedIn = this.isLoggedIn; |
|
102 let canceledMP = false; |
|
103 |
|
104 this._uiBusy = true; |
|
105 try { |
|
106 let plainOctet = this._utfConverter.ConvertFromUnicode(plainText); |
|
107 plainOctet += this._utfConverter.Finish(); |
|
108 cipherText = this._decoderRing.encryptString(plainOctet); |
|
109 } catch (e) { |
|
110 this.log("Failed to encrypt string. (" + e.name + ")"); |
|
111 // If the user clicks Cancel, we get NS_ERROR_FAILURE. |
|
112 // (unlike decrypting, which gets NS_ERROR_NOT_AVAILABLE). |
|
113 if (e.result == Cr.NS_ERROR_FAILURE) { |
|
114 canceledMP = true; |
|
115 throw Components.Exception("User canceled master password entry", Cr.NS_ERROR_ABORT); |
|
116 } else { |
|
117 throw Components.Exception("Couldn't encrypt string", Cr.NS_ERROR_FAILURE); |
|
118 } |
|
119 } finally { |
|
120 this._uiBusy = false; |
|
121 // If we triggered a master password prompt, notify observers. |
|
122 if (!wasLoggedIn && this.isLoggedIn) |
|
123 this._notifyObservers("passwordmgr-crypto-login"); |
|
124 else if (canceledMP) |
|
125 this._notifyObservers("passwordmgr-crypto-loginCanceled"); |
|
126 } |
|
127 return cipherText; |
|
128 }, |
|
129 |
|
130 |
|
131 /* |
|
132 * decrypt |
|
133 * |
|
134 * Decrypts the specified string, using the SecretDecoderRing. |
|
135 * |
|
136 * Returns the decrypted string, or throws an exception if there was a |
|
137 * problem. |
|
138 */ |
|
139 decrypt : function (cipherText) { |
|
140 let plainText = null; |
|
141 |
|
142 let wasLoggedIn = this.isLoggedIn; |
|
143 let canceledMP = false; |
|
144 |
|
145 this._uiBusy = true; |
|
146 try { |
|
147 let plainOctet; |
|
148 plainOctet = this._decoderRing.decryptString(cipherText); |
|
149 plainText = this._utfConverter.ConvertToUnicode(plainOctet); |
|
150 } catch (e) { |
|
151 this.log("Failed to decrypt string: " + cipherText + |
|
152 " (" + e.name + ")"); |
|
153 |
|
154 // In the unlikely event the converter threw, reset it. |
|
155 this._utfConverterReset(); |
|
156 |
|
157 // If the user clicks Cancel, we get NS_ERROR_NOT_AVAILABLE. |
|
158 // If the cipherText is bad / wrong key, we get NS_ERROR_FAILURE |
|
159 // Wrong passwords are handled by the decoderRing reprompting; |
|
160 // we get no notification. |
|
161 if (e.result == Cr.NS_ERROR_NOT_AVAILABLE) { |
|
162 canceledMP = true; |
|
163 throw Components.Exception("User canceled master password entry", Cr.NS_ERROR_ABORT); |
|
164 } else { |
|
165 throw Components.Exception("Couldn't decrypt string", Cr.NS_ERROR_FAILURE); |
|
166 } |
|
167 } finally { |
|
168 this._uiBusy = false; |
|
169 // If we triggered a master password prompt, notify observers. |
|
170 if (!wasLoggedIn && this.isLoggedIn) |
|
171 this._notifyObservers("passwordmgr-crypto-login"); |
|
172 else if (canceledMP) |
|
173 this._notifyObservers("passwordmgr-crypto-loginCanceled"); |
|
174 } |
|
175 |
|
176 return plainText; |
|
177 }, |
|
178 |
|
179 |
|
180 /* |
|
181 * uiBusy |
|
182 */ |
|
183 get uiBusy() { |
|
184 return this._uiBusy; |
|
185 }, |
|
186 |
|
187 |
|
188 /* |
|
189 * isLoggedIn |
|
190 */ |
|
191 get isLoggedIn() { |
|
192 let status = this._sdrSlot.status; |
|
193 this.log("SDR slot status is " + status); |
|
194 if (status == Ci.nsIPKCS11Slot.SLOT_READY || |
|
195 status == Ci.nsIPKCS11Slot.SLOT_LOGGED_IN) |
|
196 return true; |
|
197 if (status == Ci.nsIPKCS11Slot.SLOT_NOT_LOGGED_IN) |
|
198 return false; |
|
199 throw Components.Exception("unexpected slot status: " + status, Cr.NS_ERROR_FAILURE); |
|
200 }, |
|
201 |
|
202 |
|
203 /* |
|
204 * defaultEncType |
|
205 */ |
|
206 get defaultEncType() { |
|
207 return Ci.nsILoginManagerCrypto.ENCTYPE_SDR; |
|
208 }, |
|
209 |
|
210 |
|
211 /* |
|
212 * _notifyObservers |
|
213 */ |
|
214 _notifyObservers : function(topic) { |
|
215 this.log("Prompted for a master password, notifying for " + topic); |
|
216 Services.obs.notifyObservers(null, topic, null); |
|
217 }, |
|
218 }; // end of nsLoginManagerCrypto_SDR implementation |
|
219 |
|
220 let component = [LoginManagerCrypto_SDR]; |
|
221 this.NSGetFactory = XPCOMUtils.generateNSGetFactory(component); |