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  * Support routines for SECItem data structure.
7  */
8 
9 #include "seccomon.h"
10 #include "secitem.h"
11 #include "secerr.h"
12 #include "secport.h"
13 
14 SECItem *
15 SECITEM_AllocItem(PLArenaPool *arena, SECItem *item, unsigned int len)
16 {
17     SECItem *result = NULL;
18     void *mark = NULL;
19 
20     if (arena != NULL) {
21         mark = PORT_ArenaMark(arena);
22     }
23 
24     if (item == NULL) {
25         if (arena != NULL) {
26             result = PORT_ArenaZAlloc(arena, sizeof(SECItem));
27         } else {
28             result = PORT_ZAlloc(sizeof(SECItem));
29         }
30         if (result == NULL) {
31             goto loser;
32         }
33     } else {
34         PORT_Assert(item->data == NULL);
35         result = item;
36     }
37 
38     result->len = len;
39     if (len) {
40         if (arena != NULL) {
41             result->data = PORT_ArenaAlloc(arena, len);
42         } else {
43             result->data = PORT_Alloc(len);
44         }
45         if (result->data == NULL) {
46             goto loser;
47         }
48     } else {
49         result->data = NULL;
50     }
51 
52     if (mark) {
53         PORT_ArenaUnmark(arena, mark);
54     }
55     return (result);
56 
57 loser:
58     if (arena != NULL) {
59         if (mark) {
60             PORT_ArenaRelease(arena, mark);
61         }
62         if (item != NULL) {
63             item->data = NULL;
64             item->len = 0;
65         }
66     } else {
67         if (result != NULL) {
68             SECITEM_FreeItem(result, (item == NULL) ? PR_TRUE : PR_FALSE);
69         }
70         /*
71          * If item is not NULL, the above has set item->data and
72          * item->len to 0.
73          */
74     }
75     return (NULL);
76 }
77 
78 SECStatus
79 SECITEM_MakeItem(PLArenaPool *arena, SECItem *dest, const unsigned char *data,
80                  unsigned int len)
81 {
82     SECItem it = { siBuffer, (unsigned char *)data, len };
83 
84     return SECITEM_CopyItem(arena, dest, &it);
85 }
86 
87 SECStatus
88 SECITEM_ReallocItem(PLArenaPool *arena, SECItem *item, unsigned int oldlen,
89                     unsigned int newlen)
90 {
91     PORT_Assert(item != NULL);
92     if (item == NULL) {
93         /* XXX Set error.  But to what? */
94         return SECFailure;
95     }
96 
97     /*
98      * If no old length, degenerate to just plain alloc.
99      */
100     if (oldlen == 0) {
101         PORT_Assert(item->data == NULL || item->len == 0);
102         if (newlen == 0) {
103             /* Nothing to do.  Weird, but not a failure.  */
104             return SECSuccess;
105         }
106         item->len = newlen;
107         if (arena != NULL) {
108             item->data = PORT_ArenaAlloc(arena, newlen);
109         } else {
110             item->data = PORT_Alloc(newlen);
111         }
112     } else {
113         if (arena != NULL) {
114             item->data = PORT_ArenaGrow(arena, item->data, oldlen, newlen);
115         } else {
116             item->data = PORT_Realloc(item->data, newlen);
117         }
118     }
119 
120     if (item->data == NULL) {
121         return SECFailure;
122     }
123 
124     return SECSuccess;
125 }
126 
127 SECStatus
128 SECITEM_ReallocItemV2(PLArenaPool *arena, SECItem *item, unsigned int newlen)
129 {
130     unsigned char *newdata = NULL;
131 
132     PORT_Assert(item);
133     if (!item) {
134         PORT_SetError(SEC_ERROR_INVALID_ARGS);
135         return SECFailure;
136     }
137 
138     if (item->len == newlen) {
139         return SECSuccess;
140     }
141 
142     if (!newlen) {
143         if (!arena) {
144             PORT_Free(item->data);
145         }
146         item->data = NULL;
147         item->len = 0;
148         return SECSuccess;
149     }
150 
151     if (!item->data) {
152         /* allocate fresh block of memory */
153         PORT_Assert(!item->len);
154         if (arena) {
155             newdata = PORT_ArenaAlloc(arena, newlen);
156         } else {
157             newdata = PORT_Alloc(newlen);
158         }
159     } else {
160         /* reallocate or adjust existing block of memory */
161         if (arena) {
162             if (item->len > newlen) {
163                 /* There's no need to realloc a shorter block from the arena,
164                  * because it would result in using even more memory!
165                  * Therefore we'll continue to use the old block and
166                  * set the item to the shorter size.
167                  */
168                 item->len = newlen;
169                 return SECSuccess;
170             }
171             newdata = PORT_ArenaGrow(arena, item->data, item->len, newlen);
172         } else {
173             newdata = PORT_Realloc(item->data, newlen);
174         }
175     }
176 
177     if (!newdata) {
178         PORT_SetError(SEC_ERROR_NO_MEMORY);
179         return SECFailure;
180     }
181 
182     item->len = newlen;
183     item->data = newdata;
184     return SECSuccess;
185 }
186 
187 SECComparison
188 SECITEM_CompareItem(const SECItem *a, const SECItem *b)
189 {
190     unsigned m;
191     int rv;
192 
193     if (a == b)
194         return SECEqual;
195     if (!a || !a->len || !a->data)
196         return (!b || !b->len || !b->data) ? SECEqual : SECLessThan;
197     if (!b || !b->len || !b->data)
198         return SECGreaterThan;
199 
200     m = ((a->len < b->len) ? a->len : b->len);
201 
202     rv = PORT_Memcmp(a->data, b->data, m);
203     if (rv) {
204         return rv < 0 ? SECLessThan : SECGreaterThan;
205     }
206     if (a->len < b->len) {
207         return SECLessThan;
208     }
209     if (a->len == b->len) {
210         return SECEqual;
211     }
212     return SECGreaterThan;
213 }
214 
215 PRBool
216 SECITEM_ItemsAreEqual(const SECItem *a, const SECItem *b)
217 {
218     if (a->len != b->len)
219         return PR_FALSE;
220     if (!a->len)
221         return PR_TRUE;
222     if (!a->data || !b->data) {
223         /* avoid null pointer crash. */
224         return (PRBool)(a->data == b->data);
225     }
226     return (PRBool)!PORT_Memcmp(a->data, b->data, a->len);
227 }
228 
229 SECItem *
230 SECITEM_DupItem(const SECItem *from)
231 {
232     return SECITEM_ArenaDupItem(NULL, from);
233 }
234 
235 SECItem *
236 SECITEM_ArenaDupItem(PLArenaPool *arena, const SECItem *from)
237 {
238     SECItem *to;
239 
240     if (from == NULL) {
241         return (NULL);
242     }
243 
244     if (arena != NULL) {
245         to = (SECItem *)PORT_ArenaAlloc(arena, sizeof(SECItem));
246     } else {
247         to = (SECItem *)PORT_Alloc(sizeof(SECItem));
248     }
249     if (to == NULL) {
250         return (NULL);
251     }
252 
253     if (arena != NULL) {
254         to->data = (unsigned char *)PORT_ArenaAlloc(arena, from->len);
255     } else {
256         to->data = (unsigned char *)PORT_Alloc(from->len);
257     }
258     if (to->data == NULL) {
259         PORT_Free(to);
260         return (NULL);
261     }
262 
263     to->len = from->len;
264     to->type = from->type;
265     if (to->len) {
266         PORT_Memcpy(to->data, from->data, to->len);
267     }
268 
269     return (to);
270 }
271 
272 SECStatus
273 SECITEM_CopyItem(PLArenaPool *arena, SECItem *to, const SECItem *from)
274 {
275     to->type = from->type;
276     if (from->data && from->len) {
277         if (arena) {
278             to->data = (unsigned char *)PORT_ArenaAlloc(arena, from->len);
279         } else {
280             to->data = (unsigned char *)PORT_Alloc(from->len);
281         }
282 
283         if (!to->data) {
284             return SECFailure;
285         }
286         PORT_Memcpy(to->data, from->data, from->len);
287         to->len = from->len;
288     } else {
289         /*
290          * If from->data is NULL but from->len is nonzero, this function
291          * will succeed.  Is this right?
292          */
293         to->data = 0;
294         to->len = 0;
295     }
296     return SECSuccess;
297 }
298 
299 void
300 SECITEM_FreeItem(SECItem *zap, PRBool freeit)
301 {
302     if (zap) {
303         PORT_Free(zap->data);
304         zap->data = 0;
305         zap->len = 0;
306         if (freeit) {
307             PORT_Free(zap);
308         }
309     }
310 }
311 
312 void
313 SECITEM_ZfreeItem(SECItem *zap, PRBool freeit)
314 {
315     if (zap) {
316         PORT_ZFree(zap->data, zap->len);
317         zap->data = 0;
318         zap->len = 0;
319         if (freeit) {
320             PORT_ZFree(zap, sizeof(SECItem));
321         }
322     }
323 }
324 /* these reroutines were taken from pkix oid.c, which is supposed to
325  * replace this file some day */
326 /*
327  * This is the hash function.  We simply XOR the encoded form with
328  * itself in sizeof(PLHashNumber)-byte chunks.  Improving this
329  * routine is left as an excercise for the more mathematically
330  * inclined student.
331  */
332 PLHashNumber PR_CALLBACK
333 SECITEM_Hash(const void *key)
334 {
335     const SECItem *item = (const SECItem *)key;
336     PLHashNumber rv = 0;
337 
338     PRUint8 *data = (PRUint8 *)item->data;
339     PRUint32 i;
340     PRUint8 *rvc = (PRUint8 *)&rv;
341 
342     for (i = 0; i < item->len; i++) {
343         rvc[i % sizeof(rv)] ^= *data;
344         data++;
345     }
346 
347     return rv;
348 }
349 
350 /*
351  * This is the key-compare function.  It simply does a lexical
352  * comparison on the item data.  This does not result in
353  * quite the same ordering as the "sequence of numbers" order,
354  * but heck it's only used internally by the hash table anyway.
355  */
356 PRIntn PR_CALLBACK
357 SECITEM_HashCompare(const void *k1, const void *k2)
358 {
359     const SECItem *i1 = (const SECItem *)k1;
360     const SECItem *i2 = (const SECItem *)k2;
361 
362     return SECITEM_ItemsAreEqual(i1, i2);
363 }
364 
365 SECItemArray *
366 SECITEM_AllocArray(PLArenaPool *arena, SECItemArray *array, unsigned int len)
367 {
368     SECItemArray *result = NULL;
369     void *mark = NULL;
370 
371     if (array != NULL && array->items != NULL) {
372         PORT_Assert(0);
373         PORT_SetError(SEC_ERROR_INVALID_ARGS);
374         return NULL;
375     }
376 
377     if (arena != NULL) {
378         mark = PORT_ArenaMark(arena);
379     }
380 
381     if (array == NULL) {
382         if (arena != NULL) {
383             result = PORT_ArenaZAlloc(arena, sizeof(SECItemArray));
384         } else {
385             result = PORT_ZAlloc(sizeof(SECItemArray));
386         }
387         if (result == NULL) {
388             goto loser;
389         }
390     } else {
391         result = array;
392     }
393 
394     result->len = len;
395     if (len) {
396         if (arena != NULL) {
397             result->items = PORT_ArenaZNewArray(arena, SECItem, len);
398         } else {
399             result->items = PORT_ZNewArray(SECItem, len);
400         }
401         if (result->items == NULL) {
402             goto loser;
403         }
404     } else {
405         result->items = NULL;
406     }
407 
408     if (mark) {
409         PORT_ArenaUnmark(arena, mark);
410     }
411     return result;
412 
413 loser:
414     if (arena != NULL) {
415         if (mark) {
416             PORT_ArenaRelease(arena, mark);
417         }
418     } else {
419         if (result != NULL && array == NULL) {
420             PORT_Free(result);
421         }
422     }
423     if (array != NULL) {
424         array->items = NULL;
425         array->len = 0;
426     }
427     return NULL;
428 }
429 
430 static void
431 secitem_FreeArray(SECItemArray *array, PRBool zero_items, PRBool freeit)
432 {
433     unsigned int i;
434 
435     if (!array || !array->len || !array->items)
436         return;
437 
438     for (i = 0; i < array->len; ++i) {
439         SECItem *item = &array->items[i];
440 
441         if (item->data) {
442             if (zero_items) {
443                 SECITEM_ZfreeItem(item, PR_FALSE);
444             } else {
445                 SECITEM_FreeItem(item, PR_FALSE);
446             }
447         }
448     }
449     PORT_Free(array->items);
450     array->items = NULL;
451     array->len = 0;
452 
453     if (freeit)
454         PORT_Free(array);
455 }
456 
457 void
458 SECITEM_FreeArray(SECItemArray *array, PRBool freeit)
459 {
460     secitem_FreeArray(array, PR_FALSE, freeit);
461 }
462 
463 void
464 SECITEM_ZfreeArray(SECItemArray *array, PRBool freeit)
465 {
466     secitem_FreeArray(array, PR_TRUE, freeit);
467 }
468 
469 SECItemArray *
470 SECITEM_DupArray(PLArenaPool *arena, const SECItemArray *from)
471 {
472     SECItemArray *result;
473     unsigned int i;
474 
475     /* Require a "from" array.
476      * Reject an inconsistent "from" array with NULL data and nonzero length.
477      * However, allow a "from" array of zero length.
478      */
479     if (!from || (!from->items && from->len))
480         return NULL;
481 
482     result = SECITEM_AllocArray(arena, NULL, from->len);
483     if (!result)
484         return NULL;
485 
486     for (i = 0; i < from->len; ++i) {
487         SECStatus rv = SECITEM_CopyItem(arena,
488                                         &result->items[i], &from->items[i]);
489         if (rv != SECSuccess) {
490             SECITEM_ZfreeArray(result, PR_TRUE);
491             return NULL;
492         }
493     }
494 
495     return result;
496 }
497