libwbclient: Make wbcAddNamedBlob not use talloc
[samba.git] / nsswitch / libwbclient / wbc_util.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    Winbind client asynchronous API, utility functions
5
6    Copyright (C) Gerald (Jerry) Carter 2007-2008
7
8
9    This library is free software; you can redistribute it and/or
10    modify it under the terms of the GNU Lesser General Public
11    License as published by the Free Software Foundation; either
12    version 3 of the License, or (at your option) any later version.
13
14    This library is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17    Library General Public License for more details.
18
19    You should have received a copy of the GNU Lesser General Public License
20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 /* Required Headers */
24
25 #include "replace.h"
26 #include "libwbclient.h"
27 #include "../winbind_client.h"
28
29 /** @brief Ping winbindd to see if the daemon is running
30  *
31  * @return #wbcErr
32  **/
33 wbcErr wbcPing(void)
34 {
35         struct winbindd_request request;
36         struct winbindd_response response;
37
38         /* Initialize request */
39
40         ZERO_STRUCT(request);
41         ZERO_STRUCT(response);
42
43         return wbcRequestResponse(WINBINDD_PING, &request, &response);
44 }
45
46 static void wbcInterfaceDetailsDestructor(void *ptr)
47 {
48         struct wbcInterfaceDetails *i = (struct wbcInterfaceDetails *)ptr;
49         free(i->winbind_version);
50         free(i->netbios_name);
51         free(i->netbios_domain);
52         free(i->dns_domain);
53 }
54
55 /**
56  * @brief Query useful information about the winbind service
57  *
58  * @param *_details     pointer to hold the struct wbcInterfaceDetails
59  *
60  * @return #wbcErr
61  */
62
63 wbcErr wbcInterfaceDetails(struct wbcInterfaceDetails **_details)
64 {
65         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
66         struct wbcInterfaceDetails *info;
67         struct wbcDomainInfo *domain = NULL;
68         struct winbindd_request request;
69         struct winbindd_response response;
70
71         /* Initialize request */
72
73         ZERO_STRUCT(request);
74         ZERO_STRUCT(response);
75
76         info = (struct wbcInterfaceDetails *)wbcAllocateMemory(
77                 sizeof(struct wbcInterfaceDetails), 1,
78                 wbcInterfaceDetailsDestructor);
79         BAIL_ON_PTR_ERROR(info, wbc_status);
80
81         /* first the interface version */
82         wbc_status = wbcRequestResponse(WINBINDD_INTERFACE_VERSION, NULL, &response);
83         BAIL_ON_WBC_ERROR(wbc_status);
84         info->interface_version = response.data.interface_version;
85
86         /* then the samba version and the winbind separator */
87         wbc_status = wbcRequestResponse(WINBINDD_INFO, NULL, &response);
88         BAIL_ON_WBC_ERROR(wbc_status);
89
90         info->winbind_version = strdup(response.data.info.samba_version);
91         BAIL_ON_PTR_ERROR(info->winbind_version, wbc_status);
92         info->winbind_separator = response.data.info.winbind_separator;
93
94         /* then the local netbios name */
95         wbc_status = wbcRequestResponse(WINBINDD_NETBIOS_NAME, NULL, &response);
96         BAIL_ON_WBC_ERROR(wbc_status);
97
98         info->netbios_name = strdup(response.data.netbios_name);
99         BAIL_ON_PTR_ERROR(info->netbios_name, wbc_status);
100
101         /* then the local workgroup name */
102         wbc_status = wbcRequestResponse(WINBINDD_DOMAIN_NAME, NULL, &response);
103         BAIL_ON_WBC_ERROR(wbc_status);
104
105         info->netbios_domain = strdup(response.data.domain_name);
106         BAIL_ON_PTR_ERROR(info->netbios_domain, wbc_status);
107
108         wbc_status = wbcDomainInfo(info->netbios_domain, &domain);
109         if (wbc_status == WBC_ERR_DOMAIN_NOT_FOUND) {
110                 /* maybe it's a standalone server */
111                 domain = NULL;
112                 wbc_status = WBC_ERR_SUCCESS;
113         } else {
114                 BAIL_ON_WBC_ERROR(wbc_status);
115         }
116
117         if (domain) {
118                 info->dns_domain = strdup(domain->dns_name);
119                 wbcFreeMemory(domain);
120                 BAIL_ON_PTR_ERROR(info->dns_domain, wbc_status);
121         } else {
122                 info->dns_domain = NULL;
123         }
124
125         *_details = info;
126         info = NULL;
127
128         wbc_status = WBC_ERR_SUCCESS;
129
130 done:
131         wbcFreeMemory(info);
132         return wbc_status;
133 }
134
135 static void wbcDomainInfoDestructor(void *ptr)
136 {
137         struct wbcDomainInfo *i = (struct wbcDomainInfo *)ptr;
138         free(i->short_name);
139         free(i->dns_name);
140 }
141
142 /** @brief Lookup the current status of a trusted domain, sync wrapper
143  *
144  * @param domain      Domain to query
145  * @param *dinfo       Pointer to returned struct wbcDomainInfo
146  *
147  * @return #wbcErr
148  */
149
150 wbcErr wbcDomainInfo(const char *domain, struct wbcDomainInfo **dinfo)
151 {
152         struct winbindd_request request;
153         struct winbindd_response response;
154         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
155         struct wbcDomainInfo *info = NULL;
156
157         if (!domain || !dinfo) {
158                 wbc_status = WBC_ERR_INVALID_PARAM;
159                 BAIL_ON_WBC_ERROR(wbc_status);
160         }
161
162         /* Initialize request */
163
164         ZERO_STRUCT(request);
165         ZERO_STRUCT(response);
166
167         strncpy(request.domain_name, domain,
168                 sizeof(request.domain_name)-1);
169
170         wbc_status = wbcRequestResponse(WINBINDD_DOMAIN_INFO,
171                                         &request,
172                                         &response);
173         BAIL_ON_WBC_ERROR(wbc_status);
174
175         info = (struct wbcDomainInfo *)wbcAllocateMemory(
176                 sizeof(struct wbcDomainInfo), 1, wbcDomainInfoDestructor);
177         BAIL_ON_PTR_ERROR(info, wbc_status);
178
179         info->short_name = strdup(response.data.domain_info.name);
180         BAIL_ON_PTR_ERROR(info->short_name, wbc_status);
181
182         info->dns_name = strdup(response.data.domain_info.alt_name);
183         BAIL_ON_PTR_ERROR(info->dns_name, wbc_status);
184
185         wbc_status = wbcStringToSid(response.data.domain_info.sid,
186                                     &info->sid);
187         BAIL_ON_WBC_ERROR(wbc_status);
188
189         if (response.data.domain_info.native_mode)
190                 info->domain_flags |= WBC_DOMINFO_DOMAIN_NATIVE;
191         if (response.data.domain_info.active_directory)
192                 info->domain_flags |= WBC_DOMINFO_DOMAIN_AD;
193         if (response.data.domain_info.primary)
194                 info->domain_flags |= WBC_DOMINFO_DOMAIN_PRIMARY;
195
196         *dinfo = info;
197         info = NULL;
198
199         wbc_status = WBC_ERR_SUCCESS;
200
201  done:
202         wbcFreeMemory(info);
203         return wbc_status;
204 }
205
206
207 /* Resolve a NetbiosName via WINS */
208 wbcErr wbcResolveWinsByName(const char *name, char **ip)
209 {
210         struct winbindd_request request;
211         struct winbindd_response response;
212         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
213         char *ipaddr;
214
215         ZERO_STRUCT(request);
216         ZERO_STRUCT(response);
217
218         /* Send request */
219
220         strncpy(request.data.winsreq, name,
221                 sizeof(request.data.winsreq)-1);
222
223         wbc_status = wbcRequestResponse(WINBINDD_WINS_BYNAME,
224                                         &request,
225                                         &response);
226         BAIL_ON_WBC_ERROR(wbc_status);
227
228         /* Display response */
229
230         ipaddr = wbcStrDup(response.data.winsresp);
231         BAIL_ON_PTR_ERROR(ipaddr, wbc_status);
232
233         *ip = ipaddr;
234         wbc_status = WBC_ERR_SUCCESS;
235
236  done:
237         return wbc_status;
238 }
239
240 /* Resolve an IP address via WINS into a NetbiosName */
241 wbcErr wbcResolveWinsByIP(const char *ip, char **name)
242 {
243         struct winbindd_request request;
244         struct winbindd_response response;
245         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
246         char *name_str;
247
248         ZERO_STRUCT(request);
249         ZERO_STRUCT(response);
250
251         /* Send request */
252
253         strncpy(request.data.winsreq, ip,
254                 sizeof(request.data.winsreq)-1);
255
256         wbc_status = wbcRequestResponse(WINBINDD_WINS_BYIP,
257                                         &request,
258                                         &response);
259         BAIL_ON_WBC_ERROR(wbc_status);
260
261         /* Display response */
262
263         name_str = wbcStrDup(response.data.winsresp);
264         BAIL_ON_PTR_ERROR(name_str, wbc_status);
265
266         *name = name_str;
267         wbc_status = WBC_ERR_SUCCESS;
268
269  done:
270         return wbc_status;
271 }
272
273 /**
274  */
275
276 static wbcErr process_domain_info_string(TALLOC_CTX *ctx,
277                                          struct wbcDomainInfo *info,
278                                          char *info_string)
279 {
280         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
281         char *r = NULL;
282         char *s = NULL;
283
284         if (!info || !info_string) {
285                 wbc_status = WBC_ERR_INVALID_PARAM;
286                 BAIL_ON_WBC_ERROR(wbc_status);
287         }
288
289         ZERO_STRUCTP(info);
290
291         r = info_string;
292
293         /* Short Name */
294         if ((s = strchr(r, '\\')) == NULL) {
295                 wbc_status = WBC_ERR_INVALID_RESPONSE;
296                 BAIL_ON_WBC_ERROR(wbc_status);
297         }
298         *s = '\0';
299         s++;
300
301         info->short_name = talloc_strdup(ctx, r);
302         BAIL_ON_PTR_ERROR(info->short_name, wbc_status);
303
304
305         /* DNS Name */
306         r = s;
307         if ((s = strchr(r, '\\')) == NULL) {
308                 wbc_status = WBC_ERR_INVALID_RESPONSE;
309                 BAIL_ON_WBC_ERROR(wbc_status);
310         }
311         *s = '\0';
312         s++;
313
314         info->dns_name = talloc_strdup(ctx, r);
315         BAIL_ON_PTR_ERROR(info->dns_name, wbc_status);
316
317         /* SID */
318         r = s;
319         if ((s = strchr(r, '\\')) == NULL) {
320                 wbc_status = WBC_ERR_INVALID_RESPONSE;
321                 BAIL_ON_WBC_ERROR(wbc_status);
322         }
323         *s = '\0';
324         s++;
325
326         wbc_status = wbcStringToSid(r, &info->sid);
327         BAIL_ON_WBC_ERROR(wbc_status);
328
329         /* Trust type */
330         r = s;
331         if ((s = strchr(r, '\\')) == NULL) {
332                 wbc_status = WBC_ERR_INVALID_RESPONSE;
333                 BAIL_ON_WBC_ERROR(wbc_status);
334         }
335         *s = '\0';
336         s++;
337
338         if (strcmp(r, "None") == 0) {
339                 info->trust_type = WBC_DOMINFO_TRUSTTYPE_NONE;
340         } else if (strcmp(r, "External") == 0) {
341                 info->trust_type = WBC_DOMINFO_TRUSTTYPE_EXTERNAL;
342         } else if (strcmp(r, "Forest") == 0) {
343                 info->trust_type = WBC_DOMINFO_TRUSTTYPE_FOREST;
344         } else if (strcmp(r, "In Forest") == 0) {
345                 info->trust_type = WBC_DOMINFO_TRUSTTYPE_IN_FOREST;
346         } else {
347                 wbc_status = WBC_ERR_INVALID_RESPONSE;
348                 BAIL_ON_WBC_ERROR(wbc_status);
349         }
350
351         /* Transitive */
352         r = s;
353         if ((s = strchr(r, '\\')) == NULL) {
354                 wbc_status = WBC_ERR_INVALID_RESPONSE;
355                 BAIL_ON_WBC_ERROR(wbc_status);
356         }
357         *s = '\0';
358         s++;
359
360         if (strcmp(r, "Yes") == 0) {
361                 info->trust_flags |= WBC_DOMINFO_TRUST_TRANSITIVE;
362         }
363
364         /* Incoming */
365         r = s;
366         if ((s = strchr(r, '\\')) == NULL) {
367                 wbc_status = WBC_ERR_INVALID_RESPONSE;
368                 BAIL_ON_WBC_ERROR(wbc_status);
369         }
370         *s = '\0';
371         s++;
372
373         if (strcmp(r, "Yes") == 0) {
374                 info->trust_flags |= WBC_DOMINFO_TRUST_INCOMING;
375         }
376
377         /* Outgoing */
378         r = s;
379         if ((s = strchr(r, '\\')) == NULL) {
380                 wbc_status = WBC_ERR_INVALID_RESPONSE;
381                 BAIL_ON_WBC_ERROR(wbc_status);
382         }
383         *s = '\0';
384         s++;
385
386         if (strcmp(r, "Yes") == 0) {
387                 info->trust_flags |= WBC_DOMINFO_TRUST_OUTGOING;
388         }
389
390         /* Online/Offline status */
391
392         r = s;
393         if (r == NULL) {
394                 wbc_status = WBC_ERR_INVALID_RESPONSE;
395                 BAIL_ON_WBC_ERROR(wbc_status);
396         }
397         if ( strcmp(r, "Offline") == 0) {
398                 info->domain_flags |= WBC_DOMINFO_DOMAIN_OFFLINE;
399         }
400
401         wbc_status = WBC_ERR_SUCCESS;
402
403  done:
404         return wbc_status;
405 }
406
407 /* Enumerate the domain trusts known by Winbind */
408 wbcErr wbcListTrusts(struct wbcDomainInfo **domains, size_t *num_domains)
409 {
410         struct winbindd_response response;
411         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
412         char *p = NULL;
413         char *q = NULL;
414         char *extra_data = NULL;
415         int count = 0;
416         struct wbcDomainInfo *d_list = NULL;
417         int i = 0;
418
419         *domains = NULL;
420         *num_domains = 0;
421
422         ZERO_STRUCT(response);
423
424         /* Send request */
425
426         wbc_status = wbcRequestResponse(WINBINDD_LIST_TRUSTDOM,
427                                         NULL,
428                                         &response);
429         BAIL_ON_WBC_ERROR(wbc_status);
430
431         /* Decode the response */
432
433         p = (char *)response.extra_data.data;
434
435         if ((p == NULL) || (strlen(p) == 0)) {
436                 /* We should always at least get back our
437                    own SAM domain */
438
439                 wbc_status = WBC_ERR_DOMAIN_NOT_FOUND;
440                 BAIL_ON_WBC_ERROR(wbc_status);
441         }
442
443         /* Count number of domains */
444
445         count = 0;
446         while (p) {
447                 count++;
448
449                 if ((q = strchr(p, '\n')) != NULL)
450                         q++;
451                 p = q;
452         }
453
454         d_list = talloc_array(NULL, struct wbcDomainInfo, count);
455         BAIL_ON_PTR_ERROR(d_list, wbc_status);
456
457         extra_data = strdup((char*)response.extra_data.data);
458         BAIL_ON_PTR_ERROR(extra_data, wbc_status);
459
460         p = extra_data;
461
462         /* Outer loop processes the list of domain information */
463
464         for (i=0; i<count && p; i++) {
465                 char *next = strchr(p, '\n');
466
467                 if (next) {
468                         *next = '\0';
469                         next++;
470                 }
471
472                 wbc_status = process_domain_info_string(d_list, &d_list[i], p);
473                 BAIL_ON_WBC_ERROR(wbc_status);
474
475                 p = next;
476         }
477
478         *domains = d_list;
479         d_list = NULL;
480         *num_domains = i;
481
482  done:
483         winbindd_free_response(&response);
484         talloc_free(d_list);
485         free(extra_data);
486         return wbc_status;
487 }
488
489 /* Enumerate the domain trusts known by Winbind */
490 wbcErr wbcLookupDomainController(const char *domain,
491                                  uint32_t flags,
492                                 struct wbcDomainControllerInfo **dc_info)
493 {
494         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
495         struct winbindd_request request;
496         struct winbindd_response response;
497         struct wbcDomainControllerInfo *dc = NULL;
498
499         /* validate input params */
500
501         if (!domain || !dc_info) {
502                 wbc_status = WBC_ERR_INVALID_PARAM;
503                 BAIL_ON_WBC_ERROR(wbc_status);
504         }
505
506         ZERO_STRUCT(request);
507         ZERO_STRUCT(response);
508
509         strncpy(request.data.dsgetdcname.domain_name, domain,
510                 sizeof(request.data.dsgetdcname.domain_name)-1);
511
512         request.flags = flags;
513
514         dc = talloc(NULL, struct wbcDomainControllerInfo);
515         BAIL_ON_PTR_ERROR(dc, wbc_status);
516
517         /* Send request */
518
519         wbc_status = wbcRequestResponse(WINBINDD_DSGETDCNAME,
520                                         &request,
521                                         &response);
522         BAIL_ON_WBC_ERROR(wbc_status);
523
524         dc->dc_name = talloc_strdup(dc, response.data.dsgetdcname.dc_unc);
525         BAIL_ON_PTR_ERROR(dc->dc_name, wbc_status);
526
527         *dc_info = dc;
528
529 done:
530         if (!WBC_ERROR_IS_OK(wbc_status)) {
531                 talloc_free(dc);
532         }
533
534         return wbc_status;
535 }
536
537 static wbcErr wbc_create_domain_controller_info_ex(TALLOC_CTX *mem_ctx,
538                                                    const struct winbindd_response *resp,
539                                                    struct wbcDomainControllerInfoEx **_i)
540 {
541         wbcErr wbc_status = WBC_ERR_SUCCESS;
542         struct wbcDomainControllerInfoEx *i;
543         struct wbcGuid guid;
544
545         i = talloc(mem_ctx, struct wbcDomainControllerInfoEx);
546         BAIL_ON_PTR_ERROR(i, wbc_status);
547
548         i->dc_unc = talloc_strdup(i, resp->data.dsgetdcname.dc_unc);
549         BAIL_ON_PTR_ERROR(i->dc_unc, wbc_status);
550
551         i->dc_address = talloc_strdup(i, resp->data.dsgetdcname.dc_address);
552         BAIL_ON_PTR_ERROR(i->dc_address, wbc_status);
553
554         i->dc_address_type = resp->data.dsgetdcname.dc_address_type;
555
556         wbc_status = wbcStringToGuid(resp->data.dsgetdcname.domain_guid, &guid);
557         if (WBC_ERROR_IS_OK(wbc_status)) {
558                 i->domain_guid = talloc(i, struct wbcGuid);
559                 BAIL_ON_PTR_ERROR(i->domain_guid, wbc_status);
560
561                 *i->domain_guid = guid;
562         } else {
563                 i->domain_guid = NULL;
564         }
565
566         i->domain_name = talloc_strdup(i, resp->data.dsgetdcname.domain_name);
567         BAIL_ON_PTR_ERROR(i->domain_name, wbc_status);
568
569         if (resp->data.dsgetdcname.forest_name[0] != '\0') {
570                 i->forest_name = talloc_strdup(i,
571                         resp->data.dsgetdcname.forest_name);
572                 BAIL_ON_PTR_ERROR(i->forest_name, wbc_status);
573         } else {
574                 i->forest_name = NULL;
575         }
576
577         i->dc_flags = resp->data.dsgetdcname.dc_flags;
578
579         if (resp->data.dsgetdcname.dc_site_name[0] != '\0') {
580                 i->dc_site_name = talloc_strdup(i,
581                         resp->data.dsgetdcname.dc_site_name);
582                 BAIL_ON_PTR_ERROR(i->dc_site_name, wbc_status);
583         } else {
584                 i->dc_site_name = NULL;
585         }
586
587         if (resp->data.dsgetdcname.client_site_name[0] != '\0') {
588                 i->client_site_name = talloc_strdup(i,
589                         resp->data.dsgetdcname.client_site_name);
590                 BAIL_ON_PTR_ERROR(i->client_site_name, wbc_status);
591         } else {
592                 i->client_site_name = NULL;
593         }
594
595         *_i = i;
596         i = NULL;
597
598 done:
599         talloc_free(i);
600         return wbc_status;
601 }
602
603 /* Get extended domain controller information */
604 wbcErr wbcLookupDomainControllerEx(const char *domain,
605                                    struct wbcGuid *guid,
606                                    const char *site,
607                                    uint32_t flags,
608                                    struct wbcDomainControllerInfoEx **dc_info)
609 {
610         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
611         struct winbindd_request request;
612         struct winbindd_response response;
613
614         /* validate input params */
615
616         if (!domain || !dc_info) {
617                 wbc_status = WBC_ERR_INVALID_PARAM;
618                 BAIL_ON_WBC_ERROR(wbc_status);
619         }
620
621         ZERO_STRUCT(request);
622         ZERO_STRUCT(response);
623
624         request.data.dsgetdcname.flags = flags;
625
626         strncpy(request.data.dsgetdcname.domain_name, domain,
627                 sizeof(request.data.dsgetdcname.domain_name)-1);
628
629         if (site) {
630                 strncpy(request.data.dsgetdcname.site_name, site,
631                         sizeof(request.data.dsgetdcname.site_name)-1);
632         }
633
634         if (guid) {
635                 char *str = NULL;
636
637                 wbc_status = wbcGuidToString(guid, &str);
638                 BAIL_ON_WBC_ERROR(wbc_status);
639
640                 strncpy(request.data.dsgetdcname.domain_guid, str,
641                         sizeof(request.data.dsgetdcname.domain_guid)-1);
642
643                 wbcFreeMemory(str);
644         }
645
646         /* Send request */
647
648         wbc_status = wbcRequestResponse(WINBINDD_DSGETDCNAME,
649                                         &request,
650                                         &response);
651         BAIL_ON_WBC_ERROR(wbc_status);
652
653         if (dc_info) {
654                 wbc_status = wbc_create_domain_controller_info_ex(NULL,
655                                                                   &response,
656                                                                   dc_info);
657                 BAIL_ON_WBC_ERROR(wbc_status);
658         }
659
660         wbc_status = WBC_ERR_SUCCESS;
661 done:
662         return wbc_status;
663 }
664
665 static void wbcNamedBlobDestructor(void *ptr)
666 {
667         struct wbcNamedBlob *b = (struct wbcNamedBlob *)ptr;
668
669         while (b->name != NULL) {
670                 free((char *)(b->name));
671                 free(b->blob.data);
672                 b += 1;
673         }
674 }
675
676 /* Initialize a named blob and add to list of blobs */
677 wbcErr wbcAddNamedBlob(size_t *num_blobs,
678                        struct wbcNamedBlob **pblobs,
679                        const char *name,
680                        uint32_t flags,
681                        uint8_t *data,
682                        size_t length)
683 {
684         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
685         struct wbcNamedBlob *blobs, *blob;
686
687         if (name == NULL) {
688                 return WBC_ERR_INVALID_PARAM;
689         }
690
691         /*
692          * Overallocate the b->name==NULL terminator for
693          * wbcNamedBlobDestructor
694          */
695         blobs = (struct wbcNamedBlob *)wbcAllocateMemory(
696                 sizeof(struct wbcNamedBlob), *num_blobs + 2,
697                 wbcNamedBlobDestructor);
698
699         if (*pblobs != NULL) {
700                 struct wbcNamedBlob *old = *pblobs;
701                 memcpy(blobs, old, sizeof(struct wbcNamedBlob) * (*num_blobs));
702                 if (*num_blobs != 0) {
703                         /* end indicator for wbcNamedBlobDestructor */
704                         old[0].name = NULL;
705                 }
706                 wbcFreeMemory(old);
707         }
708         *pblobs = blobs;
709
710         blob = &blobs[*num_blobs];
711
712         blob->name = strdup(name);
713         BAIL_ON_PTR_ERROR(blob->name, wbc_status);
714         blob->flags = flags;
715
716         blob->blob.length = length;
717         blob->blob.data = (uint8_t *)malloc(length);
718         BAIL_ON_PTR_ERROR(blob->blob.data, wbc_status);
719         memcpy(blob->blob.data, data, length);
720
721         *num_blobs += 1;
722         *pblobs = blobs;
723         blobs = NULL;
724
725         wbc_status = WBC_ERR_SUCCESS;
726 done:
727         wbcFreeMemory(blobs);
728         return wbc_status;
729 }