]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - crypto/heimdal/lib/kadm5/init_c.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / crypto / heimdal / lib / kadm5 / init_c.c
1 /*
2  * Copyright (c) 1997 - 2006 Kungliga Tekniska Högskolan
3  * (Royal Institute of Technology, Stockholm, Sweden).
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * 3. Neither the name of the Institute nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33
34 #include "kadm5_locl.h"
35 #include <sys/types.h>
36 #ifdef HAVE_SYS_SOCKET_H
37 #include <sys/socket.h>
38 #endif
39 #ifdef HAVE_NETINET_IN_H
40 #include <netinet/in.h>
41 #endif
42 #ifdef HAVE_NETDB_H
43 #include <netdb.h>
44 #endif
45
46 RCSID("$Id$");
47
48 static void
49 set_funcs(kadm5_client_context *c)
50 {
51 #define SET(C, F) (C)->funcs.F = kadm5 ## _c_ ## F
52     SET(c, chpass_principal);
53     SET(c, chpass_principal_with_key);
54     SET(c, create_principal);
55     SET(c, delete_principal);
56     SET(c, destroy);
57     SET(c, flush);
58     SET(c, get_principal);
59     SET(c, get_principals);
60     SET(c, get_privs);
61     SET(c, modify_principal);
62     SET(c, randkey_principal);
63     SET(c, rename_principal);
64 }
65
66 kadm5_ret_t
67 _kadm5_c_init_context(kadm5_client_context **ctx,
68                       kadm5_config_params *params,
69                       krb5_context context)
70 {
71     krb5_error_code ret;
72     char *colon;
73
74     *ctx = malloc(sizeof(**ctx));
75     if(*ctx == NULL)
76         return ENOMEM;
77     memset(*ctx, 0, sizeof(**ctx));
78     krb5_add_et_list (context, initialize_kadm5_error_table_r);
79     set_funcs(*ctx);
80     (*ctx)->context = context;
81     if(params->mask & KADM5_CONFIG_REALM) {
82         ret = 0;
83         (*ctx)->realm = strdup(params->realm);
84         if ((*ctx)->realm == NULL)
85             ret = ENOMEM;
86     } else
87         ret = krb5_get_default_realm((*ctx)->context, &(*ctx)->realm);
88     if (ret) {
89         free(*ctx);
90         return ret;
91     }
92     if(params->mask & KADM5_CONFIG_ADMIN_SERVER)
93         (*ctx)->admin_server = strdup(params->admin_server);
94     else {
95         char **hostlist;
96
97         ret = krb5_get_krb_admin_hst (context, &(*ctx)->realm, &hostlist);
98         if (ret) {
99             free((*ctx)->realm);
100             free(*ctx);
101             return ret;
102         }
103         (*ctx)->admin_server = strdup(*hostlist);
104         krb5_free_krbhst (context, hostlist);
105     }
106
107     if ((*ctx)->admin_server == NULL) {
108         free((*ctx)->realm);
109         free(*ctx);
110         return ENOMEM;
111     }
112     colon = strchr ((*ctx)->admin_server, ':');
113     if (colon != NULL)
114         *colon++ = '\0';
115
116     (*ctx)->kadmind_port = 0;
117
118     if(params->mask & KADM5_CONFIG_KADMIND_PORT)
119         (*ctx)->kadmind_port = params->kadmind_port;
120     else if (colon != NULL) {
121         char *end;
122
123         (*ctx)->kadmind_port = htons(strtol (colon, &end, 0));
124     }
125     if ((*ctx)->kadmind_port == 0)
126         (*ctx)->kadmind_port = krb5_getportbyname (context, "kerberos-adm",
127                                                    "tcp", 749);
128     return 0;
129 }
130
131 static krb5_error_code
132 get_kadm_ticket(krb5_context context,
133                 krb5_ccache id,
134                 krb5_principal client,
135                 const char *server_name)
136 {
137     krb5_error_code ret;
138     krb5_creds in, *out;
139
140     memset(&in, 0, sizeof(in));
141     in.client = client;
142     ret = krb5_parse_name(context, server_name, &in.server);
143     if(ret)
144         return ret;
145     ret = krb5_get_credentials(context, 0, id, &in, &out);
146     if(ret == 0)
147         krb5_free_creds(context, out);
148     krb5_free_principal(context, in.server);
149     return ret;
150 }
151
152 static krb5_error_code
153 get_new_cache(krb5_context context,
154               krb5_principal client,
155               const char *password,
156               krb5_prompter_fct prompter,
157               const char *keytab,
158               const char *server_name,
159               krb5_ccache *ret_cache)
160 {
161     krb5_error_code ret;
162     krb5_creds cred;
163     krb5_get_init_creds_opt *opt;
164     krb5_ccache id;
165
166     ret = krb5_get_init_creds_opt_alloc (context, &opt);
167     if (ret)
168         return ret;
169
170     krb5_get_init_creds_opt_set_default_flags(context, "kadmin",
171                                               krb5_principal_get_realm(context,
172                                                                        client),
173                                               opt);
174
175
176     krb5_get_init_creds_opt_set_forwardable (opt, FALSE);
177     krb5_get_init_creds_opt_set_proxiable (opt, FALSE);
178
179     if(password == NULL && prompter == NULL) {
180         krb5_keytab kt;
181         if(keytab == NULL)
182             ret = krb5_kt_default(context, &kt);
183         else
184             ret = krb5_kt_resolve(context, keytab, &kt);
185         if(ret) {
186             krb5_get_init_creds_opt_free(context, opt);
187             return ret;
188         }
189         ret = krb5_get_init_creds_keytab (context,
190                                           &cred,
191                                           client,
192                                           kt,
193                                           0,
194                                           server_name,
195                                           opt);
196         krb5_kt_close(context, kt);
197     } else {
198         ret = krb5_get_init_creds_password (context,
199                                             &cred,
200                                             client,
201                                             password,
202                                             prompter,
203                                             NULL,
204                                             0,
205                                             server_name,
206                                             opt);
207     }
208     krb5_get_init_creds_opt_free(context, opt);
209     switch(ret){
210     case 0:
211         break;
212     case KRB5_LIBOS_PWDINTR:    /* don't print anything if it was just C-c:ed */
213     case KRB5KRB_AP_ERR_BAD_INTEGRITY:
214     case KRB5KRB_AP_ERR_MODIFIED:
215         return KADM5_BAD_PASSWORD;
216     default:
217         return ret;
218     }
219     ret = krb5_cc_new_unique(context, krb5_cc_type_memory, NULL, &id);
220     if(ret)
221         return ret;
222     ret = krb5_cc_initialize (context, id, cred.client);
223     if (ret)
224         return ret;
225     ret = krb5_cc_store_cred (context, id, &cred);
226     if (ret)
227         return ret;
228     krb5_free_cred_contents (context, &cred);
229     *ret_cache = id;
230     return 0;
231 }
232
233 /*
234  * Check the credential cache `id´ to figure out what principal to use
235  * when talking to the kadmind. If there is a initial kadmin/admin@
236  * credential in the cache, use that client principal. Otherwise, use
237  * the client principals first component and add /admin to the
238  * principal.
239  */
240
241 static krb5_error_code
242 get_cache_principal(krb5_context context,
243                     krb5_ccache *id,
244                     krb5_principal *client)
245 {
246     krb5_error_code ret;
247     const char *name, *inst;
248     krb5_principal p1, p2;
249
250     ret = krb5_cc_default(context, id);
251     if(ret) {
252         *id = NULL;
253         return ret;
254     }
255
256     ret = krb5_cc_get_principal(context, *id, &p1);
257     if(ret) {
258         krb5_cc_close(context, *id);
259         *id = NULL;
260         return ret;
261     }
262
263     ret = krb5_make_principal(context, &p2, NULL,
264                               "kadmin", "admin", NULL);
265     if (ret) {
266         krb5_cc_close(context, *id);
267         *id = NULL;
268         krb5_free_principal(context, p1);
269         return ret;
270     }
271
272     {
273         krb5_creds in, *out;
274         krb5_kdc_flags flags;
275
276         flags.i = 0;
277         memset(&in, 0, sizeof(in));
278
279         in.client = p1;
280         in.server = p2;
281
282         /* check for initial ticket kadmin/admin */
283         ret = krb5_get_credentials_with_flags(context, KRB5_GC_CACHED, flags,
284                                               *id, &in, &out);
285         krb5_free_principal(context, p2);
286         if (ret == 0) {
287             if (out->flags.b.initial) {
288                 *client = p1;
289                 krb5_free_creds(context, out);
290                 return 0;
291             }
292             krb5_free_creds(context, out);
293         }
294     }
295     krb5_cc_close(context, *id);
296     *id = NULL;
297
298     name = krb5_principal_get_comp_string(context, p1, 0);
299     inst = krb5_principal_get_comp_string(context, p1, 1);
300     if(inst == NULL || strcmp(inst, "admin") != 0) {
301         ret = krb5_make_principal(context, &p2, NULL, name, "admin", NULL);
302         krb5_free_principal(context, p1);
303         if(ret != 0)
304             return ret;
305
306         *client = p2;
307         return 0;
308     }
309
310     *client = p1;
311
312     return 0;
313 }
314
315 krb5_error_code
316 _kadm5_c_get_cred_cache(krb5_context context,
317                         const char *client_name,
318                         const char *server_name,
319                         const char *password,
320                         krb5_prompter_fct prompter,
321                         const char *keytab,
322                         krb5_ccache ccache,
323                         krb5_ccache *ret_cache)
324 {
325     krb5_error_code ret;
326     krb5_ccache id = NULL;
327     krb5_principal default_client = NULL, client = NULL;
328
329     /* treat empty password as NULL */
330     if(password && *password == '\0')
331         password = NULL;
332     if(server_name == NULL)
333         server_name = KADM5_ADMIN_SERVICE;
334
335     if(client_name != NULL) {
336         ret = krb5_parse_name(context, client_name, &client);
337         if(ret)
338             return ret;
339     }
340
341     if(ccache != NULL) {
342         id = ccache;
343         ret = krb5_cc_get_principal(context, id, &client);
344         if(ret)
345             return ret;
346     } else {
347         /* get principal from default cache, ok if this doesn't work */
348
349         ret = get_cache_principal(context, &id, &default_client);
350         if (ret) {
351             /*
352              * No client was specified by the caller and we cannot
353              * determine the client from a credentials cache.
354              */
355             const char *user;
356
357             user = get_default_username ();
358
359             if(user == NULL) {
360                 krb5_set_error_message(context, KADM5_FAILURE, "Unable to find local user name");
361                 return KADM5_FAILURE;
362             }
363             ret = krb5_make_principal(context, &default_client,
364                                       NULL, user, "admin", NULL);
365             if(ret)
366                 return ret;
367         }
368     }
369
370
371     /*
372      * No client was specified by the caller, but we have a client
373      * from the default credentials cache.
374      */
375     if (client == NULL && default_client != NULL)
376         client = default_client;
377
378
379     if(id && client && (default_client == NULL ||
380               krb5_principal_compare(context, client, default_client) != 0)) {
381         ret = get_kadm_ticket(context, id, client, server_name);
382         if(ret == 0) {
383             *ret_cache = id;
384             krb5_free_principal(context, default_client);
385             if (default_client != client)
386                 krb5_free_principal(context, client);
387             return 0;
388         }
389         if(ccache != NULL)
390             /* couldn't get ticket from cache */
391             return -1;
392     }
393     /* get creds via AS request */
394     if(id && (id != ccache))
395         krb5_cc_close(context, id);
396     if (client != default_client)
397         krb5_free_principal(context, default_client);
398
399     ret = get_new_cache(context, client, password, prompter, keytab,
400                         server_name, ret_cache);
401     krb5_free_principal(context, client);
402     return ret;
403 }
404
405 static kadm5_ret_t
406 kadm_connect(kadm5_client_context *ctx)
407 {
408     kadm5_ret_t ret;
409     krb5_principal server;
410     krb5_ccache cc;
411     rk_socket_t s = rk_INVALID_SOCKET;
412     struct addrinfo *ai, *a;
413     struct addrinfo hints;
414     int error;
415     char portstr[NI_MAXSERV];
416     char *hostname, *slash;
417     char *service_name;
418     krb5_context context = ctx->context;
419
420     memset (&hints, 0, sizeof(hints));
421     hints.ai_socktype = SOCK_STREAM;
422     hints.ai_protocol = IPPROTO_TCP;
423
424     snprintf (portstr, sizeof(portstr), "%u", ntohs(ctx->kadmind_port));
425
426     hostname = ctx->admin_server;
427     slash = strchr (hostname, '/');
428     if (slash != NULL)
429         hostname = slash + 1;
430
431     error = getaddrinfo (hostname, portstr, &hints, &ai);
432     if (error) {
433         krb5_clear_error_message(context);
434         return KADM5_BAD_SERVER_NAME;
435     }
436
437     for (a = ai; a != NULL; a = a->ai_next) {
438         s = socket (a->ai_family, a->ai_socktype, a->ai_protocol);
439         if (s < 0)
440             continue;
441         if (connect (s, a->ai_addr, a->ai_addrlen) < 0) {
442             krb5_clear_error_message(context);
443             krb5_warn (context, errno, "connect(%s)", hostname);
444             rk_closesocket (s);
445             continue;
446         }
447         break;
448     }
449     if (a == NULL) {
450         freeaddrinfo (ai);
451         krb5_clear_error_message(context);
452         krb5_warnx (context, "failed to contact %s", hostname);
453         return KADM5_FAILURE;
454     }
455     ret = _kadm5_c_get_cred_cache(context,
456                                   ctx->client_name,
457                                   ctx->service_name,
458                                   NULL, ctx->prompter, ctx->keytab,
459                                   ctx->ccache, &cc);
460
461     if(ret) {
462         freeaddrinfo (ai);
463         rk_closesocket(s);
464         return ret;
465     }
466
467     if (ctx->realm)
468         asprintf(&service_name, "%s@%s", KADM5_ADMIN_SERVICE, ctx->realm);
469     else
470         asprintf(&service_name, "%s", KADM5_ADMIN_SERVICE);
471
472     if (service_name == NULL) {
473         freeaddrinfo (ai);
474         rk_closesocket(s);
475         krb5_clear_error_message(context);
476         return ENOMEM;
477     }
478
479     ret = krb5_parse_name(context, service_name, &server);
480     free(service_name);
481     if(ret) {
482         freeaddrinfo (ai);
483         if(ctx->ccache == NULL)
484             krb5_cc_close(context, cc);
485         rk_closesocket(s);
486         return ret;
487     }
488     ctx->ac = NULL;
489
490     ret = krb5_sendauth(context, &ctx->ac, &s,
491                         KADMIN_APPL_VERSION, NULL,
492                         server, AP_OPTS_MUTUAL_REQUIRED,
493                         NULL, NULL, cc, NULL, NULL, NULL);
494     if(ret == 0) {
495         krb5_data params;
496         kadm5_config_params p;
497         memset(&p, 0, sizeof(p));
498         if(ctx->realm) {
499             p.mask |= KADM5_CONFIG_REALM;
500             p.realm = ctx->realm;
501         }
502         ret = _kadm5_marshal_params(context, &p, &params);
503
504         ret = krb5_write_priv_message(context, ctx->ac, &s, &params);
505         krb5_data_free(&params);
506         if(ret) {
507             freeaddrinfo (ai);
508             rk_closesocket(s);
509             if(ctx->ccache == NULL)
510                 krb5_cc_close(context, cc);
511             return ret;
512         }
513     } else if(ret == KRB5_SENDAUTH_BADAPPLVERS) {
514         rk_closesocket(s);
515
516         s = socket (a->ai_family, a->ai_socktype, a->ai_protocol);
517         if (s < 0) {
518             freeaddrinfo (ai);
519             krb5_clear_error_message(context);
520             return errno;
521         }
522         if (connect (s, a->ai_addr, a->ai_addrlen) < 0) {
523             rk_closesocket (s);
524             freeaddrinfo (ai);
525             krb5_clear_error_message(context);
526             return errno;
527         }
528         ret = krb5_sendauth(context, &ctx->ac, &s,
529                             KADMIN_OLD_APPL_VERSION, NULL,
530                             server, AP_OPTS_MUTUAL_REQUIRED,
531                             NULL, NULL, cc, NULL, NULL, NULL);
532     }
533     freeaddrinfo (ai);
534     if(ret) {
535         rk_closesocket(s);
536         return ret;
537     }
538
539     krb5_free_principal(context, server);
540     if(ctx->ccache == NULL)
541         krb5_cc_close(context, cc);
542     ctx->sock = s;
543
544     return 0;
545 }
546
547 kadm5_ret_t
548 _kadm5_connect(void *handle)
549 {
550     kadm5_client_context *ctx = handle;
551     if(ctx->sock == -1)
552         return kadm_connect(ctx);
553     return 0;
554 }
555
556 static kadm5_ret_t
557 kadm5_c_init_with_context(krb5_context context,
558                           const char *client_name,
559                           const char *password,
560                           krb5_prompter_fct prompter,
561                           const char *keytab,
562                           krb5_ccache ccache,
563                           const char *service_name,
564                           kadm5_config_params *realm_params,
565                           unsigned long struct_version,
566                           unsigned long api_version,
567                           void **server_handle)
568 {
569     kadm5_ret_t ret;
570     kadm5_client_context *ctx;
571     krb5_ccache cc;
572
573     ret = _kadm5_c_init_context(&ctx, realm_params, context);
574     if(ret)
575         return ret;
576
577     if(password != NULL && *password != '\0') {
578         ret = _kadm5_c_get_cred_cache(context,
579                                       client_name,
580                                       service_name,
581                                       password, prompter, keytab, ccache, &cc);
582         if(ret)
583             return ret; /* XXX */
584         ccache = cc;
585     }
586
587
588     if (client_name != NULL)
589         ctx->client_name = strdup(client_name);
590     else
591         ctx->client_name = NULL;
592     if (service_name != NULL)
593         ctx->service_name = strdup(service_name);
594     else
595         ctx->service_name = NULL;
596     ctx->prompter = prompter;
597     ctx->keytab = keytab;
598     ctx->ccache = ccache;
599     /* maybe we should copy the params here */
600     ctx->sock = -1;
601
602     *server_handle = ctx;
603     return 0;
604 }
605
606 static kadm5_ret_t
607 init_context(const char *client_name,
608              const char *password,
609              krb5_prompter_fct prompter,
610              const char *keytab,
611              krb5_ccache ccache,
612              const char *service_name,
613              kadm5_config_params *realm_params,
614              unsigned long struct_version,
615              unsigned long api_version,
616              void **server_handle)
617 {
618     krb5_context context;
619     kadm5_ret_t ret;
620     kadm5_server_context *ctx;
621
622     ret = krb5_init_context(&context);
623     if (ret)
624         return ret;
625     ret = kadm5_c_init_with_context(context,
626                                     client_name,
627                                     password,
628                                     prompter,
629                                     keytab,
630                                     ccache,
631                                     service_name,
632                                     realm_params,
633                                     struct_version,
634                                     api_version,
635                                     server_handle);
636     if(ret){
637         krb5_free_context(context);
638         return ret;
639     }
640     ctx = *server_handle;
641     ctx->my_context = 1;
642     return 0;
643 }
644
645 kadm5_ret_t
646 kadm5_c_init_with_password_ctx(krb5_context context,
647                                const char *client_name,
648                                const char *password,
649                                const char *service_name,
650                                kadm5_config_params *realm_params,
651                                unsigned long struct_version,
652                                unsigned long api_version,
653                                void **server_handle)
654 {
655     return kadm5_c_init_with_context(context,
656                                      client_name,
657                                      password,
658                                      krb5_prompter_posix,
659                                      NULL,
660                                      NULL,
661                                      service_name,
662                                      realm_params,
663                                      struct_version,
664                                      api_version,
665                                      server_handle);
666 }
667
668 kadm5_ret_t
669 kadm5_c_init_with_password(const char *client_name,
670                            const char *password,
671                            const char *service_name,
672                            kadm5_config_params *realm_params,
673                            unsigned long struct_version,
674                            unsigned long api_version,
675                            void **server_handle)
676 {
677     return init_context(client_name,
678                         password,
679                         krb5_prompter_posix,
680                         NULL,
681                         NULL,
682                         service_name,
683                         realm_params,
684                         struct_version,
685                         api_version,
686                         server_handle);
687 }
688
689 kadm5_ret_t
690 kadm5_c_init_with_skey_ctx(krb5_context context,
691                            const char *client_name,
692                            const char *keytab,
693                            const char *service_name,
694                            kadm5_config_params *realm_params,
695                            unsigned long struct_version,
696                            unsigned long api_version,
697                            void **server_handle)
698 {
699     return kadm5_c_init_with_context(context,
700                                      client_name,
701                                      NULL,
702                                      NULL,
703                                      keytab,
704                                      NULL,
705                                      service_name,
706                                      realm_params,
707                                      struct_version,
708                                      api_version,
709                                      server_handle);
710 }
711
712
713 kadm5_ret_t
714 kadm5_c_init_with_skey(const char *client_name,
715                      const char *keytab,
716                      const char *service_name,
717                      kadm5_config_params *realm_params,
718                      unsigned long struct_version,
719                      unsigned long api_version,
720                      void **server_handle)
721 {
722     return init_context(client_name,
723                         NULL,
724                         NULL,
725                         keytab,
726                         NULL,
727                         service_name,
728                         realm_params,
729                         struct_version,
730                         api_version,
731                         server_handle);
732 }
733
734 kadm5_ret_t
735 kadm5_c_init_with_creds_ctx(krb5_context context,
736                             const char *client_name,
737                             krb5_ccache ccache,
738                             const char *service_name,
739                             kadm5_config_params *realm_params,
740                             unsigned long struct_version,
741                             unsigned long api_version,
742                             void **server_handle)
743 {
744     return kadm5_c_init_with_context(context,
745                                      client_name,
746                                      NULL,
747                                      NULL,
748                                      NULL,
749                                      ccache,
750                                      service_name,
751                                      realm_params,
752                                      struct_version,
753                                      api_version,
754                                      server_handle);
755 }
756
757 kadm5_ret_t
758 kadm5_c_init_with_creds(const char *client_name,
759                         krb5_ccache ccache,
760                         const char *service_name,
761                         kadm5_config_params *realm_params,
762                         unsigned long struct_version,
763                         unsigned long api_version,
764                         void **server_handle)
765 {
766     return init_context(client_name,
767                         NULL,
768                         NULL,
769                         NULL,
770                         ccache,
771                         service_name,
772                         realm_params,
773                         struct_version,
774                         api_version,
775                         server_handle);
776 }
777
778 #if 0
779 kadm5_ret_t
780 kadm5_init(char *client_name, char *pass,
781            char *service_name,
782            kadm5_config_params *realm_params,
783            unsigned long struct_version,
784            unsigned long api_version,
785            void **server_handle)
786 {
787 }
788 #endif
789