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 #include "lgdb.h"
5 #include "secerr.h"
6 #include "lgglue.h"
7
8 /*
9 * ******************** Attribute Utilities *******************************
10 */
11
12 /*
13 * look up and attribute structure from a type and Object structure.
14 * The returned attribute is referenced and needs to be freed when
15 * it is no longer needed.
16 */
17 const CK_ATTRIBUTE *
lg_FindAttribute(CK_ATTRIBUTE_TYPE type,const CK_ATTRIBUTE * templ,CK_ULONG count)18 lg_FindAttribute(CK_ATTRIBUTE_TYPE type, const CK_ATTRIBUTE *templ,
19 CK_ULONG count)
20 {
21 unsigned int i;
22
23 for (i = 0; i < count; i++) {
24 if (templ[i].type == type) {
25 return &templ[i];
26 }
27 }
28 return NULL;
29 }
30
31 /*
32 * return true if object has attribute
33 */
34 PRBool
lg_hasAttribute(CK_ATTRIBUTE_TYPE type,const CK_ATTRIBUTE * templ,CK_ULONG count)35 lg_hasAttribute(CK_ATTRIBUTE_TYPE type, const CK_ATTRIBUTE *templ,
36 CK_ULONG count)
37 {
38 if (lg_FindAttribute(type, templ, count) == NULL) {
39 return PR_FALSE;
40 }
41 return PR_TRUE;
42 }
43
44 /*
45 * copy an attribute into a SECItem. Secitem is allocated in the specified
46 * arena.
47 */
48 CK_RV
lg_Attribute2SecItem(PLArenaPool * arena,CK_ATTRIBUTE_TYPE type,const CK_ATTRIBUTE * templ,CK_ULONG count,SECItem * item)49 lg_Attribute2SecItem(PLArenaPool *arena, CK_ATTRIBUTE_TYPE type,
50 const CK_ATTRIBUTE *templ, CK_ULONG count,
51 SECItem *item)
52 {
53 int len;
54 const CK_ATTRIBUTE *attribute;
55
56 attribute = lg_FindAttribute(type, templ, count);
57 if (attribute == NULL)
58 return CKR_TEMPLATE_INCOMPLETE;
59 len = attribute->ulValueLen;
60
61 if (arena) {
62 item->data = (unsigned char *)PORT_ArenaAlloc(arena, len);
63 } else {
64 item->data = (unsigned char *)PORT_Alloc(len);
65 }
66 if (item->data == NULL) {
67 return CKR_HOST_MEMORY;
68 }
69 item->len = len;
70 if (item->len) {
71 PORT_Memcpy(item->data, attribute->pValue, len);
72 }
73 return CKR_OK;
74 }
75
76 /*
77 * copy an unsigned attribute into a SECItem. Secitem is allocated in
78 * the specified arena.
79 */
80 CK_RV
lg_Attribute2SSecItem(PLArenaPool * arena,CK_ATTRIBUTE_TYPE type,const CK_ATTRIBUTE * templ,CK_ULONG count,SECItem * item)81 lg_Attribute2SSecItem(PLArenaPool *arena, CK_ATTRIBUTE_TYPE type,
82 const CK_ATTRIBUTE *templ, CK_ULONG count,
83 SECItem *item)
84 {
85 const CK_ATTRIBUTE *attribute;
86 item->data = NULL;
87
88 attribute = lg_FindAttribute(type, templ, count);
89 if (attribute == NULL)
90 return CKR_TEMPLATE_INCOMPLETE;
91
92 (void)SECITEM_AllocItem(arena, item, attribute->ulValueLen);
93 if (item->data == NULL) {
94 return CKR_HOST_MEMORY;
95 }
96 PORT_Memcpy(item->data, attribute->pValue, item->len);
97 return CKR_OK;
98 }
99
100 /*
101 * copy an unsigned attribute into a SECItem. Secitem is allocated in
102 * the specified arena.
103 */
104 CK_RV
lg_PrivAttr2SSecItem(PLArenaPool * arena,CK_ATTRIBUTE_TYPE type,const CK_ATTRIBUTE * templ,CK_ULONG count,SECItem * item,SDB * sdbpw)105 lg_PrivAttr2SSecItem(PLArenaPool *arena, CK_ATTRIBUTE_TYPE type,
106 const CK_ATTRIBUTE *templ, CK_ULONG count,
107 SECItem *item, SDB *sdbpw)
108 {
109 const CK_ATTRIBUTE *attribute;
110 SECItem epki, *dest = NULL;
111 SECStatus rv;
112
113 item->data = NULL;
114
115 attribute = lg_FindAttribute(type, templ, count);
116 if (attribute == NULL)
117 return CKR_TEMPLATE_INCOMPLETE;
118
119 epki.data = attribute->pValue;
120 epki.len = attribute->ulValueLen;
121
122 rv = lg_util_decrypt(sdbpw, &epki, &dest);
123 if (rv != SECSuccess) {
124 return CKR_USER_NOT_LOGGED_IN;
125 }
126 (void)SECITEM_AllocItem(arena, item, dest->len);
127 if (item->data == NULL) {
128 SECITEM_FreeItem(dest, PR_TRUE);
129 return CKR_HOST_MEMORY;
130 }
131
132 PORT_Memcpy(item->data, dest->data, item->len);
133 SECITEM_FreeItem(dest, PR_TRUE);
134 return CKR_OK;
135 }
136
137 CK_RV
lg_PrivAttr2SecItem(PLArenaPool * arena,CK_ATTRIBUTE_TYPE type,const CK_ATTRIBUTE * templ,CK_ULONG count,SECItem * item,SDB * sdbpw)138 lg_PrivAttr2SecItem(PLArenaPool *arena, CK_ATTRIBUTE_TYPE type,
139 const CK_ATTRIBUTE *templ, CK_ULONG count,
140 SECItem *item, SDB *sdbpw)
141 {
142 return lg_PrivAttr2SSecItem(arena, type, templ, count, item, sdbpw);
143 }
144
145 /*
146 * this is only valid for CK_BBOOL type attributes. Return the state
147 * of that attribute.
148 */
149 PRBool
lg_isTrue(CK_ATTRIBUTE_TYPE type,const CK_ATTRIBUTE * templ,CK_ULONG count)150 lg_isTrue(CK_ATTRIBUTE_TYPE type, const CK_ATTRIBUTE *templ, CK_ULONG count)
151 {
152 const CK_ATTRIBUTE *attribute;
153 PRBool tok = PR_FALSE;
154
155 attribute = lg_FindAttribute(type, templ, count);
156 if (attribute == NULL) {
157 return PR_FALSE;
158 }
159 tok = (PRBool)(*(CK_BBOOL *)attribute->pValue);
160
161 return tok;
162 }
163
164 /*
165 * return a null terminated string from attribute 'type'. This string
166 * is allocated and needs to be freed with PORT_Free() When complete.
167 */
168 char *
lg_getString(CK_ATTRIBUTE_TYPE type,const CK_ATTRIBUTE * templ,CK_ULONG count)169 lg_getString(CK_ATTRIBUTE_TYPE type, const CK_ATTRIBUTE *templ, CK_ULONG count)
170 {
171 const CK_ATTRIBUTE *attribute;
172 char *label = NULL;
173
174 attribute = lg_FindAttribute(type, templ, count);
175 if (attribute == NULL)
176 return NULL;
177
178 if (attribute->pValue != NULL) {
179 label = (char *)PORT_Alloc(attribute->ulValueLen + 1);
180 if (label == NULL) {
181 return NULL;
182 }
183
184 PORT_Memcpy(label, attribute->pValue, attribute->ulValueLen);
185 label[attribute->ulValueLen] = 0;
186 }
187 return label;
188 }
189
190 CK_RV
lg_GetULongAttribute(CK_ATTRIBUTE_TYPE type,const CK_ATTRIBUTE * templ,CK_ULONG count,CK_ULONG * longData)191 lg_GetULongAttribute(CK_ATTRIBUTE_TYPE type, const CK_ATTRIBUTE *templ,
192 CK_ULONG count, CK_ULONG *longData)
193 {
194 const CK_ATTRIBUTE *attribute;
195 CK_ULONG value = 0;
196 const unsigned char *data;
197 int i;
198
199 attribute = lg_FindAttribute(type, templ, count);
200 if (attribute == NULL)
201 return CKR_TEMPLATE_INCOMPLETE;
202
203 if (attribute->ulValueLen != 4) {
204 return CKR_ATTRIBUTE_VALUE_INVALID;
205 }
206 data = (const unsigned char *)attribute->pValue;
207 for (i = 0; i < 4; i++) {
208 value |= (CK_ULONG)(data[i]) << ((3 - i) * 8);
209 }
210
211 *longData = value;
212 return CKR_OK;
213 }
214
215 /*
216 * ******************** Object Utilities *******************************
217 */
218
219 SECStatus
lg_deleteTokenKeyByHandle(SDB * sdb,CK_OBJECT_HANDLE handle)220 lg_deleteTokenKeyByHandle(SDB *sdb, CK_OBJECT_HANDLE handle)
221 {
222 SECItem *item;
223 PRBool rem;
224 PLHashTable *hashTable = lg_GetHashTable(sdb);
225
226 item = (SECItem *)PL_HashTableLookup(hashTable, (void *)handle);
227 rem = PL_HashTableRemove(hashTable, (void *)handle);
228 if (rem && item) {
229 SECITEM_FreeItem(item, PR_TRUE);
230 }
231 return rem ? SECSuccess : SECFailure;
232 }
233
234 /* must be called holding lg_DBLock(sdb) */
235 static SECStatus
lg_addTokenKeyByHandle(SDB * sdb,CK_OBJECT_HANDLE handle,SECItem * key)236 lg_addTokenKeyByHandle(SDB *sdb, CK_OBJECT_HANDLE handle, SECItem *key)
237 {
238 PLHashEntry *entry;
239 SECItem *item;
240 PLHashTable *hashTable = lg_GetHashTable(sdb);
241
242 item = SECITEM_DupItem(key);
243 if (item == NULL) {
244 return SECFailure;
245 }
246 entry = PL_HashTableAdd(hashTable, (void *)handle, item);
247 if (entry == NULL) {
248 SECITEM_FreeItem(item, PR_TRUE);
249 return SECFailure;
250 }
251 return SECSuccess;
252 }
253
254 /* must be called holding lg_DBLock(sdb) */
255 const SECItem *
lg_lookupTokenKeyByHandle(SDB * sdb,CK_OBJECT_HANDLE handle)256 lg_lookupTokenKeyByHandle(SDB *sdb, CK_OBJECT_HANDLE handle)
257 {
258 PLHashTable *hashTable = lg_GetHashTable(sdb);
259 return (const SECItem *)PL_HashTableLookup(hashTable, (void *)handle);
260 }
261
262 static PRIntn
lg_freeHashItem(PLHashEntry * entry,PRIntn index,void * arg)263 lg_freeHashItem(PLHashEntry *entry, PRIntn index, void *arg)
264 {
265 SECItem *item = (SECItem *)entry->value;
266
267 SECITEM_FreeItem(item, PR_TRUE);
268 return HT_ENUMERATE_NEXT;
269 }
270
271 CK_RV
lg_ClearTokenKeyHashTable(SDB * sdb)272 lg_ClearTokenKeyHashTable(SDB *sdb)
273 {
274 PLHashTable *hashTable;
275 lg_DBLock(sdb);
276 hashTable = lg_GetHashTable(sdb);
277 PL_HashTableEnumerateEntries(hashTable, lg_freeHashItem, NULL);
278 lg_DBUnlock(sdb);
279 return CKR_OK;
280 }
281
282 /*
283 * handle Token Object stuff
284 */
285 static void
lg_XORHash(unsigned char * key,unsigned char * dbkey,int len)286 lg_XORHash(unsigned char *key, unsigned char *dbkey, int len)
287 {
288 int i;
289
290 PORT_Memset(key, 0, 4);
291
292 for (i = 0; i < len - 4; i += 4) {
293 key[0] ^= dbkey[i];
294 key[1] ^= dbkey[i + 1];
295 key[2] ^= dbkey[i + 2];
296 key[3] ^= dbkey[i + 3];
297 }
298 }
299
300 /* Make a token handle for an object and record it so we can find it again */
301 CK_OBJECT_HANDLE
lg_mkHandle(SDB * sdb,SECItem * dbKey,CK_OBJECT_HANDLE class)302 lg_mkHandle(SDB *sdb, SECItem *dbKey, CK_OBJECT_HANDLE class)
303 {
304 unsigned char hashBuf[4];
305 CK_OBJECT_HANDLE handle;
306 const SECItem *key;
307
308 handle = class;
309 /* there is only one KRL, use a fixed handle for it */
310 if (handle != LG_TOKEN_KRL_HANDLE) {
311 lg_XORHash(hashBuf, dbKey->data, dbKey->len);
312 handle = ((CK_OBJECT_HANDLE)hashBuf[0] << 24) |
313 ((CK_OBJECT_HANDLE)hashBuf[1] << 16) |
314 ((CK_OBJECT_HANDLE)hashBuf[2] << 8) |
315 (CK_OBJECT_HANDLE)hashBuf[3];
316 handle = class | (handle & ~(LG_TOKEN_TYPE_MASK | LG_TOKEN_MASK));
317 /* we have a CRL who's handle has randomly matched the reserved KRL
318 * handle, increment it */
319 if (handle == LG_TOKEN_KRL_HANDLE) {
320 handle++;
321 }
322 }
323
324 lg_DBLock(sdb);
325 while ((key = lg_lookupTokenKeyByHandle(sdb, handle)) != NULL) {
326 if (SECITEM_ItemsAreEqual(key, dbKey)) {
327 lg_DBUnlock(sdb);
328 return handle;
329 }
330 handle++;
331 }
332 lg_addTokenKeyByHandle(sdb, handle, dbKey);
333 lg_DBUnlock(sdb);
334 return handle;
335 }
336
337 PRBool
lg_poisonHandle(SDB * sdb,SECItem * dbKey,CK_OBJECT_HANDLE class)338 lg_poisonHandle(SDB *sdb, SECItem *dbKey, CK_OBJECT_HANDLE class)
339 {
340 unsigned char hashBuf[4];
341 CK_OBJECT_HANDLE handle;
342 const SECItem *key;
343
344 handle = class;
345 /* there is only one KRL, use a fixed handle for it */
346 if (handle != LG_TOKEN_KRL_HANDLE) {
347 lg_XORHash(hashBuf, dbKey->data, dbKey->len);
348 handle = (hashBuf[0] << 24) | (hashBuf[1] << 16) |
349 (hashBuf[2] << 8) | hashBuf[3];
350 handle = class | (handle & ~(LG_TOKEN_TYPE_MASK | LG_TOKEN_MASK));
351 /* we have a CRL who's handle has randomly matched the reserved KRL
352 * handle, increment it */
353 if (handle == LG_TOKEN_KRL_HANDLE) {
354 handle++;
355 }
356 }
357 lg_DBLock(sdb);
358 while ((key = lg_lookupTokenKeyByHandle(sdb, handle)) != NULL) {
359 if (SECITEM_ItemsAreEqual(key, dbKey)) {
360 key->data[0] ^= 0x80;
361 lg_DBUnlock(sdb);
362 return PR_TRUE;
363 }
364 handle++;
365 }
366 lg_DBUnlock(sdb);
367 return PR_FALSE;
368 }
369
370 static LGEncryptFunc lg_encrypt_stub = NULL;
371 static LGDecryptFunc lg_decrypt_stub = NULL;
372
373 void
legacy_SetCryptFunctions(LGEncryptFunc enc,LGDecryptFunc dec)374 legacy_SetCryptFunctions(LGEncryptFunc enc, LGDecryptFunc dec)
375 {
376 lg_encrypt_stub = enc;
377 lg_decrypt_stub = dec;
378 }
379
380 SECStatus
lg_util_encrypt(PLArenaPool * arena,SDB * sdb,SECItem * plainText,SECItem ** cipherText)381 lg_util_encrypt(PLArenaPool *arena, SDB *sdb,
382 SECItem *plainText, SECItem **cipherText)
383 {
384 if (lg_encrypt_stub == NULL) {
385 PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
386 return SECFailure;
387 }
388 return (*lg_encrypt_stub)(arena, sdb, plainText, cipherText);
389 }
390
391 SECStatus
lg_util_decrypt(SDB * sdb,SECItem * cipherText,SECItem ** plainText)392 lg_util_decrypt(SDB *sdb, SECItem *cipherText, SECItem **plainText)
393 {
394 if (lg_decrypt_stub == NULL) {
395 PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
396 return SECFailure;
397 }
398 return (*lg_decrypt_stub)(sdb, cipherText, plainText);
399 }
400