e2e657a903af441d5859133f6e0ad3d88b56bfcd
[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                 1, sizeof(struct wbcInterfaceDetails),
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                 1, sizeof(struct wbcDomainInfo), 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(struct wbcDomainInfo *info,
277                                          char *info_string)
278 {
279         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
280         char *r = NULL;
281         char *s = NULL;
282
283         r = info_string;
284
285         /* Short Name */
286         if ((s = strchr(r, '\\')) == NULL) {
287                 wbc_status = WBC_ERR_INVALID_RESPONSE;
288                 BAIL_ON_WBC_ERROR(wbc_status);
289         }
290         *s = '\0';
291         s++;
292
293         info->short_name = strdup(r);
294         BAIL_ON_PTR_ERROR(info->short_name, wbc_status);
295
296
297         /* DNS Name */
298         r = s;
299         if ((s = strchr(r, '\\')) == NULL) {
300                 wbc_status = WBC_ERR_INVALID_RESPONSE;
301                 BAIL_ON_WBC_ERROR(wbc_status);
302         }
303         *s = '\0';
304         s++;
305
306         info->dns_name = strdup(r);
307         BAIL_ON_PTR_ERROR(info->dns_name, wbc_status);
308
309         /* SID */
310         r = s;
311         if ((s = strchr(r, '\\')) == NULL) {
312                 wbc_status = WBC_ERR_INVALID_RESPONSE;
313                 BAIL_ON_WBC_ERROR(wbc_status);
314         }
315         *s = '\0';
316         s++;
317
318         wbc_status = wbcStringToSid(r, &info->sid);
319         BAIL_ON_WBC_ERROR(wbc_status);
320
321         /* Trust type */
322         r = s;
323         if ((s = strchr(r, '\\')) == NULL) {
324                 wbc_status = WBC_ERR_INVALID_RESPONSE;
325                 BAIL_ON_WBC_ERROR(wbc_status);
326         }
327         *s = '\0';
328         s++;
329
330         if (strcmp(r, "None") == 0) {
331                 info->trust_type = WBC_DOMINFO_TRUSTTYPE_NONE;
332         } else if (strcmp(r, "External") == 0) {
333                 info->trust_type = WBC_DOMINFO_TRUSTTYPE_EXTERNAL;
334         } else if (strcmp(r, "Forest") == 0) {
335                 info->trust_type = WBC_DOMINFO_TRUSTTYPE_FOREST;
336         } else if (strcmp(r, "In Forest") == 0) {
337                 info->trust_type = WBC_DOMINFO_TRUSTTYPE_IN_FOREST;
338         } else {
339                 wbc_status = WBC_ERR_INVALID_RESPONSE;
340                 BAIL_ON_WBC_ERROR(wbc_status);
341         }
342
343         /* Transitive */
344         r = s;
345         if ((s = strchr(r, '\\')) == NULL) {
346                 wbc_status = WBC_ERR_INVALID_RESPONSE;
347                 BAIL_ON_WBC_ERROR(wbc_status);
348         }
349         *s = '\0';
350         s++;
351
352         if (strcmp(r, "Yes") == 0) {
353                 info->trust_flags |= WBC_DOMINFO_TRUST_TRANSITIVE;
354         }
355
356         /* Incoming */
357         r = s;
358         if ((s = strchr(r, '\\')) == NULL) {
359                 wbc_status = WBC_ERR_INVALID_RESPONSE;
360                 BAIL_ON_WBC_ERROR(wbc_status);
361         }
362         *s = '\0';
363         s++;
364
365         if (strcmp(r, "Yes") == 0) {
366                 info->trust_flags |= WBC_DOMINFO_TRUST_INCOMING;
367         }
368
369         /* Outgoing */
370         r = s;
371         if ((s = strchr(r, '\\')) == NULL) {
372                 wbc_status = WBC_ERR_INVALID_RESPONSE;
373                 BAIL_ON_WBC_ERROR(wbc_status);
374         }
375         *s = '\0';
376         s++;
377
378         if (strcmp(r, "Yes") == 0) {
379                 info->trust_flags |= WBC_DOMINFO_TRUST_OUTGOING;
380         }
381
382         /* Online/Offline status */
383
384         r = s;
385         if (r == NULL) {
386                 wbc_status = WBC_ERR_INVALID_RESPONSE;
387                 BAIL_ON_WBC_ERROR(wbc_status);
388         }
389         if ( strcmp(r, "Offline") == 0) {
390                 info->domain_flags |= WBC_DOMINFO_DOMAIN_OFFLINE;
391         }
392
393         wbc_status = WBC_ERR_SUCCESS;
394
395  done:
396         return wbc_status;
397 }
398
399 static void wbcDomainInfoListDestructor(void *ptr)
400 {
401         struct wbcDomainInfo *i = (struct wbcDomainInfo *)ptr;
402
403         while (i->short_name != NULL) {
404                 free(i->short_name);
405                 free(i->dns_name);
406                 i += 1;
407         }
408 }
409
410 /* Enumerate the domain trusts known by Winbind */
411 wbcErr wbcListTrusts(struct wbcDomainInfo **domains, size_t *num_domains)
412 {
413         struct winbindd_response response;
414         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
415         char *p = NULL;
416         char *extra_data = NULL;
417         struct wbcDomainInfo *d_list = NULL;
418         int i = 0;
419
420         *domains = NULL;
421         *num_domains = 0;
422
423         ZERO_STRUCT(response);
424
425         /* Send request */
426
427         wbc_status = wbcRequestResponse(WINBINDD_LIST_TRUSTDOM,
428                                         NULL,
429                                         &response);
430         BAIL_ON_WBC_ERROR(wbc_status);
431
432         /* Decode the response */
433
434         p = (char *)response.extra_data.data;
435
436         if ((p == NULL) || (strlen(p) == 0)) {
437                 /* We should always at least get back our
438                    own SAM domain */
439
440                 wbc_status = WBC_ERR_DOMAIN_NOT_FOUND;
441                 BAIL_ON_WBC_ERROR(wbc_status);
442         }
443
444         d_list = (struct wbcDomainInfo *)wbcAllocateMemory(
445                 response.data.num_entries + 1,sizeof(struct wbcDomainInfo),
446                 wbcDomainInfoListDestructor);
447         BAIL_ON_PTR_ERROR(d_list, wbc_status);
448
449         extra_data = strdup((char*)response.extra_data.data);
450         BAIL_ON_PTR_ERROR(extra_data, wbc_status);
451
452         p = extra_data;
453
454         /* Outer loop processes the list of domain information */
455
456         for (i=0; i<response.data.num_entries && p; i++) {
457                 char *next = strchr(p, '\n');
458
459                 if (next) {
460                         *next = '\0';
461                         next++;
462                 }
463
464                 wbc_status = process_domain_info_string(&d_list[i], p);
465                 BAIL_ON_WBC_ERROR(wbc_status);
466
467                 p = next;
468         }
469
470         *domains = d_list;
471         d_list = NULL;
472         *num_domains = i;
473
474  done:
475         winbindd_free_response(&response);
476         wbcFreeMemory(d_list);
477         free(extra_data);
478         return wbc_status;
479 }
480
481 static void wbcDomainControllerInfoDestructor(void *ptr)
482 {
483         struct wbcDomainControllerInfo *i =
484                 (struct wbcDomainControllerInfo *)ptr;
485         free(i->dc_name);
486 }
487
488 /* Enumerate the domain trusts known by Winbind */
489 wbcErr wbcLookupDomainController(const char *domain,
490                                  uint32_t flags,
491                                 struct wbcDomainControllerInfo **dc_info)
492 {
493         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
494         struct winbindd_request request;
495         struct winbindd_response response;
496         struct wbcDomainControllerInfo *dc = NULL;
497
498         /* validate input params */
499
500         if (!domain || !dc_info) {
501                 wbc_status = WBC_ERR_INVALID_PARAM;
502                 BAIL_ON_WBC_ERROR(wbc_status);
503         }
504
505         ZERO_STRUCT(request);
506         ZERO_STRUCT(response);
507
508         strncpy(request.data.dsgetdcname.domain_name, domain,
509                 sizeof(request.data.dsgetdcname.domain_name)-1);
510
511         request.flags = flags;
512
513         dc = (struct wbcDomainControllerInfo *)wbcAllocateMemory(
514                  1, sizeof(struct wbcDomainControllerInfo),
515                 wbcDomainControllerInfoDestructor);
516         BAIL_ON_PTR_ERROR(dc, wbc_status);
517
518         /* Send request */
519
520         wbc_status = wbcRequestResponse(WINBINDD_DSGETDCNAME,
521                                         &request,
522                                         &response);
523         BAIL_ON_WBC_ERROR(wbc_status);
524
525         dc->dc_name = strdup(response.data.dsgetdcname.dc_unc);
526         BAIL_ON_PTR_ERROR(dc->dc_name, wbc_status);
527
528         *dc_info = dc;
529         dc = NULL;
530
531 done:
532         wbcFreeMemory(dc);
533         return wbc_status;
534 }
535
536 static void wbcDomainControllerInfoExDestructor(void *ptr)
537 {
538         struct wbcDomainControllerInfoEx *i =
539                 (struct wbcDomainControllerInfoEx *)ptr;
540         free((char *)(i->dc_unc));
541         free((char *)(i->dc_address));
542         free((char *)(i->domain_guid));
543         free((char *)(i->domain_name));
544         free((char *)(i->forest_name));
545         free((char *)(i->dc_site_name));
546         free((char *)(i->client_site_name));
547 }
548
549 static wbcErr wbc_create_domain_controller_info_ex(const struct winbindd_response *resp,
550                                                    struct wbcDomainControllerInfoEx **_i)
551 {
552         wbcErr wbc_status = WBC_ERR_SUCCESS;
553         struct wbcDomainControllerInfoEx *i;
554         struct wbcGuid guid;
555
556         i = (struct wbcDomainControllerInfoEx *)wbcAllocateMemory(
557                 1, sizeof(struct wbcDomainControllerInfoEx),
558                 wbcDomainControllerInfoExDestructor);
559         BAIL_ON_PTR_ERROR(i, wbc_status);
560
561         i->dc_unc = strdup(resp->data.dsgetdcname.dc_unc);
562         BAIL_ON_PTR_ERROR(i->dc_unc, wbc_status);
563
564         i->dc_address = strdup(resp->data.dsgetdcname.dc_address);
565         BAIL_ON_PTR_ERROR(i->dc_address, wbc_status);
566
567         i->dc_address_type = resp->data.dsgetdcname.dc_address_type;
568
569         wbc_status = wbcStringToGuid(resp->data.dsgetdcname.domain_guid, &guid);
570         if (WBC_ERROR_IS_OK(wbc_status)) {
571                 i->domain_guid = (struct wbcGuid *)malloc(
572                         sizeof(struct wbcGuid));
573                 BAIL_ON_PTR_ERROR(i->domain_guid, wbc_status);
574
575                 *i->domain_guid = guid;
576         }
577
578         i->domain_name = strdup(resp->data.dsgetdcname.domain_name);
579         BAIL_ON_PTR_ERROR(i->domain_name, wbc_status);
580
581         if (resp->data.dsgetdcname.forest_name[0] != '\0') {
582                 i->forest_name = strdup(resp->data.dsgetdcname.forest_name);
583                 BAIL_ON_PTR_ERROR(i->forest_name, wbc_status);
584         }
585
586         i->dc_flags = resp->data.dsgetdcname.dc_flags;
587
588         if (resp->data.dsgetdcname.dc_site_name[0] != '\0') {
589                 i->dc_site_name = strdup(resp->data.dsgetdcname.dc_site_name);
590                 BAIL_ON_PTR_ERROR(i->dc_site_name, wbc_status);
591         }
592
593         if (resp->data.dsgetdcname.client_site_name[0] != '\0') {
594                 i->client_site_name = strdup(
595                         resp->data.dsgetdcname.client_site_name);
596                 BAIL_ON_PTR_ERROR(i->client_site_name, wbc_status);
597         }
598
599         *_i = i;
600         i = NULL;
601
602 done:
603         if (i != NULL) {
604                 wbcFreeMemory(i);
605         }
606         return wbc_status;
607 }
608
609 /* Get extended domain controller information */
610 wbcErr wbcLookupDomainControllerEx(const char *domain,
611                                    struct wbcGuid *guid,
612                                    const char *site,
613                                    uint32_t flags,
614                                    struct wbcDomainControllerInfoEx **dc_info)
615 {
616         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
617         struct winbindd_request request;
618         struct winbindd_response response;
619
620         /* validate input params */
621
622         if (!domain || !dc_info) {
623                 wbc_status = WBC_ERR_INVALID_PARAM;
624                 BAIL_ON_WBC_ERROR(wbc_status);
625         }
626
627         ZERO_STRUCT(request);
628         ZERO_STRUCT(response);
629
630         request.data.dsgetdcname.flags = flags;
631
632         strncpy(request.data.dsgetdcname.domain_name, domain,
633                 sizeof(request.data.dsgetdcname.domain_name)-1);
634
635         if (site) {
636                 strncpy(request.data.dsgetdcname.site_name, site,
637                         sizeof(request.data.dsgetdcname.site_name)-1);
638         }
639
640         if (guid) {
641                 char *str = NULL;
642
643                 wbc_status = wbcGuidToString(guid, &str);
644                 BAIL_ON_WBC_ERROR(wbc_status);
645
646                 strncpy(request.data.dsgetdcname.domain_guid, str,
647                         sizeof(request.data.dsgetdcname.domain_guid)-1);
648
649                 wbcFreeMemory(str);
650         }
651
652         /* Send request */
653
654         wbc_status = wbcRequestResponse(WINBINDD_DSGETDCNAME,
655                                         &request,
656                                         &response);
657         BAIL_ON_WBC_ERROR(wbc_status);
658
659         if (dc_info) {
660                 wbc_status = wbc_create_domain_controller_info_ex(&response,
661                                                                   dc_info);
662                 BAIL_ON_WBC_ERROR(wbc_status);
663         }
664
665         wbc_status = WBC_ERR_SUCCESS;
666 done:
667         return wbc_status;
668 }
669
670 static void wbcNamedBlobDestructor(void *ptr)
671 {
672         struct wbcNamedBlob *b = (struct wbcNamedBlob *)ptr;
673
674         while (b->name != NULL) {
675                 free((char *)(b->name));
676                 free(b->blob.data);
677                 b += 1;
678         }
679 }
680
681 /* Initialize a named blob and add to list of blobs */
682 wbcErr wbcAddNamedBlob(size_t *num_blobs,
683                        struct wbcNamedBlob **pblobs,
684                        const char *name,
685                        uint32_t flags,
686                        uint8_t *data,
687                        size_t length)
688 {
689         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
690         struct wbcNamedBlob *blobs, *blob;
691
692         if (name == NULL) {
693                 return WBC_ERR_INVALID_PARAM;
694         }
695
696         /*
697          * Overallocate the b->name==NULL terminator for
698          * wbcNamedBlobDestructor
699          */
700         blobs = (struct wbcNamedBlob *)wbcAllocateMemory(
701                 *num_blobs + 2, sizeof(struct wbcNamedBlob),
702                 wbcNamedBlobDestructor);
703
704         if (*pblobs != NULL) {
705                 struct wbcNamedBlob *old = *pblobs;
706                 memcpy(blobs, old, sizeof(struct wbcNamedBlob) * (*num_blobs));
707                 if (*num_blobs != 0) {
708                         /* end indicator for wbcNamedBlobDestructor */
709                         old[0].name = NULL;
710                 }
711                 wbcFreeMemory(old);
712         }
713         *pblobs = blobs;
714
715         blob = &blobs[*num_blobs];
716
717         blob->name = strdup(name);
718         BAIL_ON_PTR_ERROR(blob->name, wbc_status);
719         blob->flags = flags;
720
721         blob->blob.length = length;
722         blob->blob.data = (uint8_t *)malloc(length);
723         BAIL_ON_PTR_ERROR(blob->blob.data, wbc_status);
724         memcpy(blob->blob.data, data, length);
725
726         *num_blobs += 1;
727         *pblobs = blobs;
728         blobs = NULL;
729
730         wbc_status = WBC_ERR_SUCCESS;
731 done:
732         wbcFreeMemory(blobs);
733         return wbc_status;
734 }