libwbclient: Make wbcLookupDomainController not use talloc
[ira/wip.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 static void wbcDomainControllerInfoDestructor(void *ptr)
490 {
491         struct wbcDomainControllerInfo *i =
492                 (struct wbcDomainControllerInfo *)ptr;
493         free(i->dc_name);
494 }
495
496 /* Enumerate the domain trusts known by Winbind */
497 wbcErr wbcLookupDomainController(const char *domain,
498                                  uint32_t flags,
499                                 struct wbcDomainControllerInfo **dc_info)
500 {
501         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
502         struct winbindd_request request;
503         struct winbindd_response response;
504         struct wbcDomainControllerInfo *dc = NULL;
505
506         /* validate input params */
507
508         if (!domain || !dc_info) {
509                 wbc_status = WBC_ERR_INVALID_PARAM;
510                 BAIL_ON_WBC_ERROR(wbc_status);
511         }
512
513         ZERO_STRUCT(request);
514         ZERO_STRUCT(response);
515
516         strncpy(request.data.dsgetdcname.domain_name, domain,
517                 sizeof(request.data.dsgetdcname.domain_name)-1);
518
519         request.flags = flags;
520
521         dc = (struct wbcDomainControllerInfo *)wbcAllocateMemory(
522                 sizeof(struct wbcDomainControllerInfo), 1,
523                 wbcDomainControllerInfoDestructor);
524         BAIL_ON_PTR_ERROR(dc, wbc_status);
525
526         /* Send request */
527
528         wbc_status = wbcRequestResponse(WINBINDD_DSGETDCNAME,
529                                         &request,
530                                         &response);
531         BAIL_ON_WBC_ERROR(wbc_status);
532
533         dc->dc_name = strdup(response.data.dsgetdcname.dc_unc);
534         BAIL_ON_PTR_ERROR(dc->dc_name, wbc_status);
535
536         *dc_info = dc;
537         dc = NULL;
538
539 done:
540         wbcFreeMemory(dc);
541         return wbc_status;
542 }
543
544 static wbcErr wbc_create_domain_controller_info_ex(TALLOC_CTX *mem_ctx,
545                                                    const struct winbindd_response *resp,
546                                                    struct wbcDomainControllerInfoEx **_i)
547 {
548         wbcErr wbc_status = WBC_ERR_SUCCESS;
549         struct wbcDomainControllerInfoEx *i;
550         struct wbcGuid guid;
551
552         i = talloc(mem_ctx, struct wbcDomainControllerInfoEx);
553         BAIL_ON_PTR_ERROR(i, wbc_status);
554
555         i->dc_unc = talloc_strdup(i, resp->data.dsgetdcname.dc_unc);
556         BAIL_ON_PTR_ERROR(i->dc_unc, wbc_status);
557
558         i->dc_address = talloc_strdup(i, resp->data.dsgetdcname.dc_address);
559         BAIL_ON_PTR_ERROR(i->dc_address, wbc_status);
560
561         i->dc_address_type = resp->data.dsgetdcname.dc_address_type;
562
563         wbc_status = wbcStringToGuid(resp->data.dsgetdcname.domain_guid, &guid);
564         if (WBC_ERROR_IS_OK(wbc_status)) {
565                 i->domain_guid = talloc(i, struct wbcGuid);
566                 BAIL_ON_PTR_ERROR(i->domain_guid, wbc_status);
567
568                 *i->domain_guid = guid;
569         } else {
570                 i->domain_guid = NULL;
571         }
572
573         i->domain_name = talloc_strdup(i, resp->data.dsgetdcname.domain_name);
574         BAIL_ON_PTR_ERROR(i->domain_name, wbc_status);
575
576         if (resp->data.dsgetdcname.forest_name[0] != '\0') {
577                 i->forest_name = talloc_strdup(i,
578                         resp->data.dsgetdcname.forest_name);
579                 BAIL_ON_PTR_ERROR(i->forest_name, wbc_status);
580         } else {
581                 i->forest_name = NULL;
582         }
583
584         i->dc_flags = resp->data.dsgetdcname.dc_flags;
585
586         if (resp->data.dsgetdcname.dc_site_name[0] != '\0') {
587                 i->dc_site_name = talloc_strdup(i,
588                         resp->data.dsgetdcname.dc_site_name);
589                 BAIL_ON_PTR_ERROR(i->dc_site_name, wbc_status);
590         } else {
591                 i->dc_site_name = NULL;
592         }
593
594         if (resp->data.dsgetdcname.client_site_name[0] != '\0') {
595                 i->client_site_name = talloc_strdup(i,
596                         resp->data.dsgetdcname.client_site_name);
597                 BAIL_ON_PTR_ERROR(i->client_site_name, wbc_status);
598         } else {
599                 i->client_site_name = NULL;
600         }
601
602         *_i = i;
603         i = NULL;
604
605 done:
606         talloc_free(i);
607         return wbc_status;
608 }
609
610 /* Get extended domain controller information */
611 wbcErr wbcLookupDomainControllerEx(const char *domain,
612                                    struct wbcGuid *guid,
613                                    const char *site,
614                                    uint32_t flags,
615                                    struct wbcDomainControllerInfoEx **dc_info)
616 {
617         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
618         struct winbindd_request request;
619         struct winbindd_response response;
620
621         /* validate input params */
622
623         if (!domain || !dc_info) {
624                 wbc_status = WBC_ERR_INVALID_PARAM;
625                 BAIL_ON_WBC_ERROR(wbc_status);
626         }
627
628         ZERO_STRUCT(request);
629         ZERO_STRUCT(response);
630
631         request.data.dsgetdcname.flags = flags;
632
633         strncpy(request.data.dsgetdcname.domain_name, domain,
634                 sizeof(request.data.dsgetdcname.domain_name)-1);
635
636         if (site) {
637                 strncpy(request.data.dsgetdcname.site_name, site,
638                         sizeof(request.data.dsgetdcname.site_name)-1);
639         }
640
641         if (guid) {
642                 char *str = NULL;
643
644                 wbc_status = wbcGuidToString(guid, &str);
645                 BAIL_ON_WBC_ERROR(wbc_status);
646
647                 strncpy(request.data.dsgetdcname.domain_guid, str,
648                         sizeof(request.data.dsgetdcname.domain_guid)-1);
649
650                 wbcFreeMemory(str);
651         }
652
653         /* Send request */
654
655         wbc_status = wbcRequestResponse(WINBINDD_DSGETDCNAME,
656                                         &request,
657                                         &response);
658         BAIL_ON_WBC_ERROR(wbc_status);
659
660         if (dc_info) {
661                 wbc_status = wbc_create_domain_controller_info_ex(NULL,
662                                                                   &response,
663                                                                   dc_info);
664                 BAIL_ON_WBC_ERROR(wbc_status);
665         }
666
667         wbc_status = WBC_ERR_SUCCESS;
668 done:
669         return wbc_status;
670 }
671
672 static void wbcNamedBlobDestructor(void *ptr)
673 {
674         struct wbcNamedBlob *b = (struct wbcNamedBlob *)ptr;
675
676         while (b->name != NULL) {
677                 free((char *)(b->name));
678                 free(b->blob.data);
679                 b += 1;
680         }
681 }
682
683 /* Initialize a named blob and add to list of blobs */
684 wbcErr wbcAddNamedBlob(size_t *num_blobs,
685                        struct wbcNamedBlob **pblobs,
686                        const char *name,
687                        uint32_t flags,
688                        uint8_t *data,
689                        size_t length)
690 {
691         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
692         struct wbcNamedBlob *blobs, *blob;
693
694         if (name == NULL) {
695                 return WBC_ERR_INVALID_PARAM;
696         }
697
698         /*
699          * Overallocate the b->name==NULL terminator for
700          * wbcNamedBlobDestructor
701          */
702         blobs = (struct wbcNamedBlob *)wbcAllocateMemory(
703                 sizeof(struct wbcNamedBlob), *num_blobs + 2,
704                 wbcNamedBlobDestructor);
705
706         if (*pblobs != NULL) {
707                 struct wbcNamedBlob *old = *pblobs;
708                 memcpy(blobs, old, sizeof(struct wbcNamedBlob) * (*num_blobs));
709                 if (*num_blobs != 0) {
710                         /* end indicator for wbcNamedBlobDestructor */
711                         old[0].name = NULL;
712                 }
713                 wbcFreeMemory(old);
714         }
715         *pblobs = blobs;
716
717         blob = &blobs[*num_blobs];
718
719         blob->name = strdup(name);
720         BAIL_ON_PTR_ERROR(blob->name, wbc_status);
721         blob->flags = flags;
722
723         blob->blob.length = length;
724         blob->blob.data = (uint8_t *)malloc(length);
725         BAIL_ON_PTR_ERROR(blob->blob.data, wbc_status);
726         memcpy(blob->blob.data, data, length);
727
728         *num_blobs += 1;
729         *pblobs = blobs;
730         blobs = NULL;
731
732         wbc_status = WBC_ERR_SUCCESS;
733 done:
734         wbcFreeMemory(blobs);
735         return wbc_status;
736 }