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