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