s3: Add wbinfo --dc-info
[obnox/samba-ctdb.git] / 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 /* Lookup the current status of a trusted domain */
123 wbcErr wbcDomainInfo(const char *domain, struct wbcDomainInfo **dinfo)
124 {
125         struct winbindd_request request;
126         struct winbindd_response response;
127         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
128         struct wbcDomainInfo *info = NULL;
129
130         if (!domain || !dinfo) {
131                 wbc_status = WBC_ERR_INVALID_PARAM;
132                 BAIL_ON_WBC_ERROR(wbc_status);
133         }
134
135         /* Initialize request */
136
137         ZERO_STRUCT(request);
138         ZERO_STRUCT(response);
139
140         strncpy(request.domain_name, domain,
141                 sizeof(request.domain_name)-1);
142
143         wbc_status = wbcRequestResponse(WINBINDD_DOMAIN_INFO,
144                                         &request,
145                                         &response);
146         BAIL_ON_WBC_ERROR(wbc_status);
147
148         info = talloc(NULL, struct wbcDomainInfo);
149         BAIL_ON_PTR_ERROR(info, wbc_status);
150
151         info->short_name = talloc_strdup(info,
152                                          response.data.domain_info.name);
153         BAIL_ON_PTR_ERROR(info->short_name, wbc_status);
154
155         info->dns_name = talloc_strdup(info,
156                                        response.data.domain_info.alt_name);
157         BAIL_ON_PTR_ERROR(info->dns_name, wbc_status);
158
159         wbc_status = wbcStringToSid(response.data.domain_info.sid,
160                                     &info->sid);
161         BAIL_ON_WBC_ERROR(wbc_status);
162
163         if (response.data.domain_info.native_mode)
164                 info->domain_flags |= WBC_DOMINFO_DOMAIN_NATIVE;
165         if (response.data.domain_info.active_directory)
166                 info->domain_flags |= WBC_DOMINFO_DOMAIN_AD;
167         if (response.data.domain_info.primary)
168                 info->domain_flags |= WBC_DOMINFO_DOMAIN_PRIMARY;
169
170         *dinfo = info;
171
172         wbc_status = WBC_ERR_SUCCESS;
173
174  done:
175         if (!WBC_ERROR_IS_OK(wbc_status)) {
176                 talloc_free(info);
177         }
178
179         return wbc_status;
180 }
181
182 /* Get the list of current DCs */
183 wbcErr wbcDcInfo(const char *domain, size_t *num_dcs,
184                  const char ***dc_names, const char ***dc_ips)
185 {
186         struct winbindd_request request;
187         struct winbindd_response response;
188         const char **names = NULL;
189         const char **ips = NULL;
190         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
191         size_t extra_len;
192         int i;
193         char *p;
194
195         /* Initialise request */
196
197         ZERO_STRUCT(request);
198         ZERO_STRUCT(response);
199
200         if (domain != NULL) {
201                 strncpy(request.domain_name, domain,
202                         sizeof(request.domain_name) - 1);
203         }
204
205         wbc_status = wbcRequestResponse(WINBINDD_DC_INFO,
206                                         &request, &response);
207         BAIL_ON_WBC_ERROR(wbc_status);
208
209         names = talloc_zero_array(NULL, const char *,
210                                   response.data.num_entries);
211         BAIL_ON_PTR_ERROR(names, wbc_status);
212
213         ips = talloc_zero_array(NULL, const char *,
214                                 response.data.num_entries);
215         BAIL_ON_PTR_ERROR(names, wbc_status);
216
217         wbc_status = WBC_ERR_INVALID_RESPONSE;
218
219         p = (char *)response.extra_data.data;
220
221         if (response.length < (sizeof(struct winbindd_response)+1)) {
222                 goto done;
223         }
224
225         extra_len = response.length - sizeof(struct winbindd_response);
226
227         if (p[extra_len-1] != '\0') {
228                 goto done;
229         }
230
231         for (i=0; i<response.data.num_entries; i++) {
232                 char *q;
233
234                 q = strchr(p, '\n');
235                 if (q == NULL) {
236                         goto done;
237                 }
238                 names[i] = talloc_strndup(names, p, q-p);
239                 BAIL_ON_PTR_ERROR(names[i], wbc_status);
240                 p = q+1;
241
242                 q = strchr(p, '\n');
243                 if (q == NULL) {
244                         goto done;
245                 }
246                 ips[i] = talloc_strndup(ips, p, q-p);
247                 BAIL_ON_PTR_ERROR(ips[i], wbc_status);
248                 p = q+1;
249         }
250         if (p[0] != '\0') {
251                 goto done;
252         }
253
254         wbc_status = WBC_ERR_SUCCESS;
255 done:
256         if (response.extra_data.data)
257                 free(response.extra_data.data);
258
259         if (WBC_ERROR_IS_OK(wbc_status)) {
260                 *num_dcs = response.data.num_entries;
261                 *dc_names = names;
262                 names = NULL;
263                 *dc_ips = ips;
264                 ips = NULL;
265         }
266         wbcFreeMemory(names);
267         wbcFreeMemory(ips);
268         return wbc_status;
269 }
270
271 /* Resolve a NetbiosName via WINS */
272 wbcErr wbcResolveWinsByName(const char *name, char **ip)
273 {
274         struct winbindd_request request;
275         struct winbindd_response response;
276         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
277         char *ipaddr;
278
279         ZERO_STRUCT(request);
280         ZERO_STRUCT(response);
281
282         /* Send request */
283
284         strncpy(request.data.winsreq, name,
285                 sizeof(request.data.winsreq)-1);
286
287         wbc_status = wbcRequestResponse(WINBINDD_WINS_BYNAME,
288                                         &request,
289                                         &response);
290         BAIL_ON_WBC_ERROR(wbc_status);
291
292         /* Display response */
293
294         ipaddr = talloc_strdup(NULL, response.data.winsresp);
295         BAIL_ON_PTR_ERROR(ipaddr, wbc_status);
296
297         *ip = ipaddr;
298         wbc_status = WBC_ERR_SUCCESS;
299
300  done:
301         return wbc_status;
302 }
303
304 /* Resolve an IP address via WINS into a NetbiosName */
305 wbcErr wbcResolveWinsByIP(const char *ip, char **name)
306 {
307         struct winbindd_request request;
308         struct winbindd_response response;
309         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
310         char *name_str;
311
312         ZERO_STRUCT(request);
313         ZERO_STRUCT(response);
314
315         /* Send request */
316
317         strncpy(request.data.winsreq, ip,
318                 sizeof(request.data.winsreq)-1);
319
320         wbc_status = wbcRequestResponse(WINBINDD_WINS_BYIP,
321                                         &request,
322                                         &response);
323         BAIL_ON_WBC_ERROR(wbc_status);
324
325         /* Display response */
326
327         name_str = talloc_strdup(NULL, response.data.winsresp);
328         BAIL_ON_PTR_ERROR(name_str, wbc_status);
329
330         *name = name_str;
331         wbc_status = WBC_ERR_SUCCESS;
332
333  done:
334         return wbc_status;
335 }
336
337 /**
338  */
339
340 static wbcErr process_domain_info_string(TALLOC_CTX *ctx,
341                                          struct wbcDomainInfo *info,
342                                          char *info_string)
343 {
344         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
345         char *r = NULL;
346         char *s = NULL;
347
348         if (!info || !info_string) {
349                 wbc_status = WBC_ERR_INVALID_PARAM;
350                 BAIL_ON_WBC_ERROR(wbc_status);
351         }
352
353         ZERO_STRUCTP(info);
354
355         r = info_string;
356
357         /* Short Name */
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         info->short_name = talloc_strdup(ctx, r);
366         BAIL_ON_PTR_ERROR(info->short_name, wbc_status);
367
368
369         /* DNS Name */
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         info->dns_name = talloc_strdup(ctx, r);
379         BAIL_ON_PTR_ERROR(info->dns_name, wbc_status);
380
381         /* SID */
382         r = s;
383         if ((s = strchr(r, '\\')) == NULL) {
384                 wbc_status = WBC_ERR_INVALID_RESPONSE;
385                 BAIL_ON_WBC_ERROR(wbc_status);
386         }
387         *s = '\0';
388         s++;
389
390         wbc_status = wbcStringToSid(r, &info->sid);
391         BAIL_ON_WBC_ERROR(wbc_status);
392
393         /* Trust type */
394         r = s;
395         if ((s = strchr(r, '\\')) == NULL) {
396                 wbc_status = WBC_ERR_INVALID_RESPONSE;
397                 BAIL_ON_WBC_ERROR(wbc_status);
398         }
399         *s = '\0';
400         s++;
401
402         if (strcmp(r, "None") == 0) {
403                 info->trust_type = WBC_DOMINFO_TRUSTTYPE_NONE;
404         } else if (strcmp(r, "External") == 0) {
405                 info->trust_type = WBC_DOMINFO_TRUSTTYPE_EXTERNAL;
406         } else if (strcmp(r, "Forest") == 0) {
407                 info->trust_type = WBC_DOMINFO_TRUSTTYPE_FOREST;
408         } else if (strcmp(r, "In Forest") == 0) {
409                 info->trust_type = WBC_DOMINFO_TRUSTTYPE_IN_FOREST;
410         } else {
411                 wbc_status = WBC_ERR_INVALID_RESPONSE;
412                 BAIL_ON_WBC_ERROR(wbc_status);
413         }
414
415         /* Transitive */
416         r = s;
417         if ((s = strchr(r, '\\')) == NULL) {
418                 wbc_status = WBC_ERR_INVALID_RESPONSE;
419                 BAIL_ON_WBC_ERROR(wbc_status);
420         }
421         *s = '\0';
422         s++;
423
424         if (strcmp(r, "Yes") == 0) {
425                 info->trust_flags |= WBC_DOMINFO_TRUST_TRANSITIVE;
426         }
427
428         /* Incoming */
429         r = s;
430         if ((s = strchr(r, '\\')) == NULL) {
431                 wbc_status = WBC_ERR_INVALID_RESPONSE;
432                 BAIL_ON_WBC_ERROR(wbc_status);
433         }
434         *s = '\0';
435         s++;
436
437         if (strcmp(r, "Yes") == 0) {
438                 info->trust_flags |= WBC_DOMINFO_TRUST_INCOMING;
439         }
440
441         /* Outgoing */
442         r = s;
443         if ((s = strchr(r, '\\')) == NULL) {
444                 wbc_status = WBC_ERR_INVALID_RESPONSE;
445                 BAIL_ON_WBC_ERROR(wbc_status);
446         }
447         *s = '\0';
448         s++;
449
450         if (strcmp(r, "Yes") == 0) {
451                 info->trust_flags |= WBC_DOMINFO_TRUST_OUTGOING;
452         }
453
454         /* Online/Offline status */
455
456         r = s;
457         if (r == NULL) {
458                 wbc_status = WBC_ERR_INVALID_RESPONSE;
459                 BAIL_ON_WBC_ERROR(wbc_status);
460         }
461         if ( strcmp(r, "Offline") == 0) {
462                 info->domain_flags |= WBC_DOMINFO_DOMAIN_OFFLINE;
463         }
464
465         wbc_status = WBC_ERR_SUCCESS;
466
467  done:
468         return wbc_status;
469 }
470
471 /* Enumerate the domain trusts known by Winbind */
472 wbcErr wbcListTrusts(struct wbcDomainInfo **domains, size_t *num_domains)
473 {
474         struct winbindd_response response;
475         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
476         char *p = NULL;
477         char *q = NULL;
478         char *extra_data = NULL;
479         int count = 0;
480         struct wbcDomainInfo *d_list = NULL;
481         int i = 0;
482
483         *domains = NULL;
484         *num_domains = 0;
485
486         ZERO_STRUCT(response);
487
488         /* Send request */
489
490         wbc_status = wbcRequestResponse(WINBINDD_LIST_TRUSTDOM,
491                                         NULL,
492                                         &response);
493         BAIL_ON_WBC_ERROR(wbc_status);
494
495         /* Decode the response */
496
497         p = (char *)response.extra_data.data;
498
499         if (strlen(p) == 0) {
500                 /* We should always at least get back our
501                    own SAM domain */
502
503                 wbc_status = WBC_ERR_DOMAIN_NOT_FOUND;
504                 BAIL_ON_WBC_ERROR(wbc_status);
505         }
506
507         /* Count number of domains */
508
509         count = 0;
510         while (p) {
511                 count++;
512
513                 if ((q = strchr(p, '\n')) != NULL)
514                         q++;
515                 p = q;
516         }
517
518         d_list = talloc_array(NULL, struct wbcDomainInfo, count);
519         BAIL_ON_PTR_ERROR(d_list, wbc_status);
520
521         extra_data = strdup((char*)response.extra_data.data);
522         BAIL_ON_PTR_ERROR(extra_data, wbc_status);
523
524         p = extra_data;
525
526         /* Outer loop processes the list of domain information */
527
528         for (i=0; i<count && p; i++) {
529                 char *next = strchr(p, '\n');
530
531                 if (next) {
532                         *next = '\0';
533                         next++;
534                 }
535
536                 wbc_status = process_domain_info_string(d_list, &d_list[i], p);
537                 BAIL_ON_WBC_ERROR(wbc_status);
538
539                 p = next;
540         }
541
542         *domains = d_list;
543         *num_domains = i;
544
545  done:
546         if (!WBC_ERROR_IS_OK(wbc_status)) {
547                 if (d_list)
548                         talloc_free(d_list);
549                 if (extra_data)
550                         free(extra_data);
551         }
552
553         return wbc_status;
554 }
555
556 /* Enumerate the domain trusts known by Winbind */
557 wbcErr wbcLookupDomainController(const char *domain,
558                                  uint32_t flags,
559                                 struct wbcDomainControllerInfo **dc_info)
560 {
561         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
562         struct winbindd_request request;
563         struct winbindd_response response;
564         struct wbcDomainControllerInfo *dc = NULL;
565
566         /* validate input params */
567
568         if (!domain || !dc_info) {
569                 wbc_status = WBC_ERR_INVALID_PARAM;
570                 BAIL_ON_WBC_ERROR(wbc_status);
571         }
572
573         ZERO_STRUCT(request);
574         ZERO_STRUCT(response);
575
576         strncpy(request.domain_name, domain, sizeof(request.domain_name)-1);
577
578         request.flags = flags;
579
580         dc = talloc(NULL, struct wbcDomainControllerInfo);
581         BAIL_ON_PTR_ERROR(dc, wbc_status);
582
583         /* Send request */
584
585         wbc_status = wbcRequestResponse(WINBINDD_DSGETDCNAME,
586                                         &request,
587                                         &response);
588         BAIL_ON_WBC_ERROR(wbc_status);
589
590         dc->dc_name = talloc_strdup(dc, response.data.dc_name);
591         BAIL_ON_PTR_ERROR(dc->dc_name, wbc_status);
592
593         *dc_info = dc;
594
595 done:
596         if (!WBC_ERROR_IS_OK(wbc_status)) {
597                 talloc_free(dc);
598         }
599
600         return wbc_status;
601 }
602
603 static wbcErr wbc_create_domain_controller_info_ex(TALLOC_CTX *mem_ctx,
604                                                    const struct winbindd_response *resp,
605                                                    struct wbcDomainControllerInfoEx **_i)
606 {
607         wbcErr wbc_status = WBC_ERR_SUCCESS;
608         struct wbcDomainControllerInfoEx *i;
609         struct wbcGuid guid;
610
611         i = talloc(mem_ctx, struct wbcDomainControllerInfoEx);
612         BAIL_ON_PTR_ERROR(i, wbc_status);
613
614         i->dc_unc = talloc_strdup(i, resp->data.dsgetdcname.dc_unc);
615         BAIL_ON_PTR_ERROR(i->dc_unc, wbc_status);
616
617         i->dc_address = talloc_strdup(i, resp->data.dsgetdcname.dc_address);
618         BAIL_ON_PTR_ERROR(i->dc_address, wbc_status);
619
620         i->dc_address_type = resp->data.dsgetdcname.dc_address_type;
621
622         wbc_status = wbcStringToGuid(resp->data.dsgetdcname.domain_guid, &guid);
623         if (WBC_ERROR_IS_OK(wbc_status)) {
624                 i->domain_guid = talloc(i, struct wbcGuid);
625                 BAIL_ON_PTR_ERROR(i->domain_guid, wbc_status);
626
627                 *i->domain_guid = guid;
628         } else {
629                 i->domain_guid = NULL;
630         }
631
632         i->domain_name = talloc_strdup(i, resp->data.dsgetdcname.domain_name);
633         BAIL_ON_PTR_ERROR(i->domain_name, wbc_status);
634
635         if (resp->data.dsgetdcname.forest_name[0] != '\0') {
636                 i->forest_name = talloc_strdup(i,
637                         resp->data.dsgetdcname.forest_name);
638                 BAIL_ON_PTR_ERROR(i->forest_name, wbc_status);
639         } else {
640                 i->forest_name = NULL;
641         }
642
643         i->dc_flags = resp->data.dsgetdcname.dc_flags;
644
645         if (resp->data.dsgetdcname.dc_site_name[0] != '\0') {
646                 i->dc_site_name = talloc_strdup(i,
647                         resp->data.dsgetdcname.dc_site_name);
648                 BAIL_ON_PTR_ERROR(i->dc_site_name, wbc_status);
649         } else {
650                 i->dc_site_name = NULL;
651         }
652
653         if (resp->data.dsgetdcname.client_site_name[0] != '\0') {
654                 i->client_site_name = talloc_strdup(i,
655                         resp->data.dsgetdcname.client_site_name);
656                 BAIL_ON_PTR_ERROR(i->client_site_name, wbc_status);
657         } else {
658                 i->client_site_name = NULL;
659         }
660
661         *_i = i;
662         i = NULL;
663
664 done:
665         talloc_free(i);
666         return wbc_status;
667 }
668
669 /* Get extended domain controller information */
670 wbcErr wbcLookupDomainControllerEx(const char *domain,
671                                    struct wbcGuid *guid,
672                                    const char *site,
673                                    uint32_t flags,
674                                    struct wbcDomainControllerInfoEx **dc_info)
675 {
676         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
677         struct winbindd_request request;
678         struct winbindd_response response;
679
680         /* validate input params */
681
682         if (!domain || !dc_info) {
683                 wbc_status = WBC_ERR_INVALID_PARAM;
684                 BAIL_ON_WBC_ERROR(wbc_status);
685         }
686
687         ZERO_STRUCT(request);
688         ZERO_STRUCT(response);
689
690         request.data.dsgetdcname.flags = flags;
691
692         strncpy(request.data.dsgetdcname.domain_name, domain,
693                 sizeof(request.data.dsgetdcname.domain_name)-1);
694
695         if (site) {
696                 strncpy(request.data.dsgetdcname.site_name, site,
697                         sizeof(request.data.dsgetdcname.site_name)-1);
698         }
699
700         if (guid) {
701                 char *str = NULL;
702
703                 wbc_status = wbcGuidToString(guid, &str);
704                 BAIL_ON_WBC_ERROR(wbc_status);
705
706                 strncpy(request.data.dsgetdcname.domain_guid, str,
707                         sizeof(request.data.dsgetdcname.domain_guid)-1);
708
709                 wbcFreeMemory(str);
710         }
711
712         /* Send request */
713
714         wbc_status = wbcRequestResponse(WINBINDD_DSGETDCNAME,
715                                         &request,
716                                         &response);
717         BAIL_ON_WBC_ERROR(wbc_status);
718
719         if (dc_info) {
720                 wbc_status = wbc_create_domain_controller_info_ex(NULL,
721                                                                   &response,
722                                                                   dc_info);
723                 BAIL_ON_WBC_ERROR(wbc_status);
724         }
725
726         wbc_status = WBC_ERR_SUCCESS;
727 done:
728         return wbc_status;
729 }
730
731 /* Initialize a named blob and add to list of blobs */
732 wbcErr wbcAddNamedBlob(size_t *num_blobs,
733                        struct wbcNamedBlob **blobs,
734                        const char *name,
735                        uint32_t flags,
736                        uint8_t *data,
737                        size_t length)
738 {
739         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
740         struct wbcNamedBlob blob;
741
742         *blobs = talloc_realloc(NULL, *blobs, struct wbcNamedBlob,
743                                 *(num_blobs)+1);
744         BAIL_ON_PTR_ERROR(*blobs, wbc_status);
745
746         blob.name               = talloc_strdup(*blobs, name);
747         BAIL_ON_PTR_ERROR(blob.name, wbc_status);
748         blob.flags              = flags;
749         blob.blob.length        = length;
750         blob.blob.data          = (uint8_t *)talloc_memdup(*blobs, data, length);
751         BAIL_ON_PTR_ERROR(blob.blob.data, wbc_status);
752
753         (*(blobs))[*num_blobs] = blob;
754         *(num_blobs) += 1;
755
756         wbc_status = WBC_ERR_SUCCESS;
757 done:
758         if (!WBC_ERROR_IS_OK(wbc_status) && blobs) {
759                 wbcFreeMemory(*blobs);
760         }
761         return wbc_status;
762 }