Moved these administrative protocol functions out of libkrb5
authorPaul Park <pjpark@mit.edu>
Fri, 28 Apr 1995 19:16:55 +0000 (19:16 +0000)
committerPaul Park <pjpark@mit.edu>
Fri, 28 Apr 1995 19:16:55 +0000 (19:16 +0000)
git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@5622 dc483132-0cff-0310-8789-dd5450dbe970

src/lib/kadm/.Sanitize [new file with mode: 0644]
src/lib/kadm/.cvsignore [new file with mode: 0644]
src/lib/kadm/ChangeLog [new file with mode: 0644]
src/lib/kadm/Makefile.in [new file with mode: 0644]
src/lib/kadm/adm_conn.c [new file with mode: 0644]
src/lib/kadm/adm_rw.c [new file with mode: 0644]
src/lib/kadm/configure.in [new file with mode: 0644]

diff --git a/src/lib/kadm/.Sanitize b/src/lib/kadm/.Sanitize
new file mode 100644 (file)
index 0000000..9106451
--- /dev/null
@@ -0,0 +1,37 @@
+# Sanitize.in for Kerberos V5
+
+# Each directory to survive it's way into a release will need a file
+# like this one called "./.Sanitize".  All keyword lines must exist,
+# and must exist in the order specified by this file.  Each directory
+# in the tree will be processed, top down, in the following order.
+
+# Hash started lines like this one are comments and will be deleted
+# before anything else is done.  Blank lines will also be squashed
+# out.
+
+# The lines between the "Do-first:" line and the "Things-to-keep:"
+# line are executed as a /bin/sh shell script before anything else is
+# done in this 
+
+Do-first:
+
+# All files listed between the "Things-to-keep:" line and the
+# "Files-to-sed:" line will be kept.  All other files will be removed.
+# Directories listed in this section will have their own Sanitize
+# called.  Directories not listed will be removed in their entirety
+# with rm -rf.
+
+Things-to-keep:
+
+.cvsignore
+ChangeLog
+Makefile.in
+adm_conn.c
+adm_rw.c
+configure.in
+
+Things-to-lose:
+
+Do-last:
+
+# End of file.
diff --git a/src/lib/kadm/.cvsignore b/src/lib/kadm/.cvsignore
new file mode 100644 (file)
index 0000000..e8c05a6
--- /dev/null
@@ -0,0 +1 @@
+configure
diff --git a/src/lib/kadm/ChangeLog b/src/lib/kadm/ChangeLog
new file mode 100644 (file)
index 0000000..b490928
--- /dev/null
@@ -0,0 +1,9 @@
+
+Fri Apr 28 09:47:29 EDT 1995   Paul Park       (pjpark@mit.edu)
+
+       Create a new library consisting of functions which communicate with
+       the administrative server here.  These modules replace the originals
+       which used to be in libkrb5.
+
+       adm_rw.c        - Remove ntohl/htonl in favor of byte blasting.
+
diff --git a/src/lib/kadm/Makefile.in b/src/lib/kadm/Makefile.in
new file mode 100644 (file)
index 0000000..53e19f0
--- /dev/null
@@ -0,0 +1,25 @@
+CFLAGS = $(CCOPTS) $(DEFS) 
+LDFLAGS = -g
+
+all:: $(OBJS)
+
+OBJS=  adm_conn.o      \
+       adm_rw.o
+
+SRCS=  $(srcdir)/adm_conn.c    \
+       $(srcdir)/adm_rw.c
+
+libkadm.a: ${OBJS}
+       $(RM) $@
+       $(ARADD) $@ $(OBJS)
+       $(RANLIB) $@
+       $(RM) $(TOPLIBD)/libkadm.a
+       $(CP) libkadm.a $(BUILDTOP)/lib/libkadm.a
+       $(RANLIB) $(BUILDTOP)/lib/libkadm.a
+
+install:: libkadm.a
+       $(INSTALL_DATA) libkadm.a $(KRB5_LIBDIR)/libkadm.a
+       $(RANLIB) $(KRB5_LIBDIR)/libkadm.a
+
+clean::
+       $(RM) libkadm.a
diff --git a/src/lib/kadm/adm_conn.c b/src/lib/kadm/adm_conn.c
new file mode 100644 (file)
index 0000000..5761acf
--- /dev/null
@@ -0,0 +1,508 @@
+/*
+ * lib/kadm/adm_conn.c
+ *
+ * Copyright 1995 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ *
+ */
+/*
+ * Routines to contact an administrative protocol server.
+ */
+#define        NEED_SOCKETS
+#define        NEED_LOWLEVEL_IO
+#include "k5-int.h"
+#include "adm.h"
+#include "adm_proto.h"
+
+#if    HAVE_PWD_H
+#include <pwd.h>
+#endif /* HAVE_PWD_H */
+
+/*
+ * Strings
+ */
+static char *kadm_cache_name_fmt =     "FILE:/tmp/tkt_kpw_%d";
+\f
+/*
+ * kadm_get_ccache()   - Initialze a credentials cache.
+ * 
+ * Cleanup after success by calling krb5_cc_destroy() and krb5_free_principal()
+ * Allocates new ccache and client.
+ */
+static krb5_error_code
+kadm_get_ccache(kcontext, user, ccache, client)
+    krb5_context       kcontext;
+    char               *user;
+    krb5_ccache                *ccache;
+    krb5_principal     *client;
+{
+    krb5_error_code    kret;
+    char               *name;
+    int                did_malloc = 0;
+    char               new_cache[MAXPATHLEN];
+
+    /* Initialize. */
+    *client = (krb5_principal) NULL;
+
+    /*
+     * If a name specified, then use that one, else get it from our
+     * current uid.
+     */
+    if (user) {
+       name = user;
+    }
+    else {
+#if    HAVE_PWD_H
+       struct passwd *pw;
+
+       pw = getpwuid(getuid());
+       if (pw) {
+           name = (char *) malloc(strlen(pw->pw_name)+1);
+           did_malloc = 1;
+           strcpy(name, pw->pw_name);
+       }
+       else {
+           kret = errno;
+           goto cleanup;
+       }
+#else  /* HAVE_PWD_H */
+       kret = ENOENT;
+       goto cleanup;
+#endif /* HAVE_PWD_H */
+    }
+
+    /* Parse the name and form our principal */
+    if (kret = krb5_parse_name(kcontext, name, client))
+       goto cleanup;
+
+    (void) sprintf(new_cache, kadm_cache_name_fmt, getpid());
+    if (kret = krb5_cc_resolve(kcontext, new_cache, ccache))
+       goto cleanup;
+
+    kret = krb5_cc_initialize(kcontext, *ccache, *client);
+
+ cleanup:
+    if (did_malloc)
+       free(name);
+
+    if (kret) {
+       if (*client)
+           krb5_free_principal(kcontext, *client);
+    }
+
+    return(kret);
+}
+\f
+/*
+ * kadm_get_creds()    - Get initial credentials.
+ *
+ * Cleanup after success by calling krb5_free_principal().
+ * Allocates new principal for creds->server.
+ */
+static krb5_error_code
+kadm_get_creds(kcontext, ccache, client, creds, prompt, oldpw)
+    krb5_context       kcontext;
+    krb5_ccache                ccache;
+    krb5_principal     client;
+    krb5_creds         *creds;
+    char               *prompt;
+    char               *oldpw;
+{
+    char               *client_name;
+    krb5_error_code    kret;
+    krb5_address       **my_addresses;
+    int                        old_pwsize;
+
+    /* Initialize */
+    my_addresses = (krb5_address **) NULL;
+    client_name = (char *) NULL;
+
+    /* Get the string form for our principal */
+    if (kret = krb5_unparse_name(kcontext, client, &client_name))
+       return(kret);
+
+    if (kret = krb5_os_localaddr(&my_addresses))
+       goto cleanup;
+
+    creds->client = client;
+    /*
+     * Build server principal name:
+     * "changepw" is service
+     * realm name is instance
+     * realm name is realm name
+     */
+    if (kret = krb5_build_principal_ext(kcontext,
+                                       &creds->server,
+                                       client->realm.length,
+                                       client->realm.data,
+                                       strlen(KRB5_ADM_SERVICE_NAME),
+                                       KRB5_ADM_SERVICE_NAME,
+                                       client->realm.length,
+                                       client->realm.data,
+                                       0))
+       goto cleanup;
+
+    if (prompt != (char *) NULL) {
+       /* Read the password */
+       old_pwsize = KRB5_ADM_MAX_PASSWORD_LEN;
+       if (kret = krb5_read_password(kcontext,
+                                     prompt,
+                                     (char *) NULL,
+                                     oldpw,
+                                     &old_pwsize))
+           goto cleanup;
+    }
+
+    /* Get our initial ticket */
+    kret = krb5_get_in_tkt_with_password(kcontext,
+                                        0,
+                                        my_addresses,
+                                        NULL,
+                                        NULL,
+                                        oldpw,
+                                        ccache,
+                                        creds,
+                                        0);
+
+ cleanup:
+    if (kret) {
+       if (creds->server)
+           krb5_free_principal(kcontext, creds->server);
+    }
+    if (my_addresses)
+       krb5_free_addresses(kcontext, my_addresses);
+    if (client_name)
+       krb5_xfree(client_name);
+    return(kret);
+}
+\f
+/*
+ * kadm_contact_server()       - Establish a connection to the server.
+ *
+ * Cleanup after success by calling close() and free().
+ * Opens/connects socket *sockp.  Allocates address storage for local/remote.
+ */
+static krb5_error_code
+kadm_contact_server(kcontext, realmp, sockp, local, remote)
+    krb5_context       kcontext;
+    krb5_data          *realmp;
+    int                        *sockp;
+    krb5_address       **local;
+    krb5_address       **remote;
+{
+    struct hostent     *remote_host;
+    struct servent     *service;
+    char               **hostlist;
+    int                        i, count;
+
+    krb5_error_code    kret;
+
+    struct sockaddr_in in_local;
+    struct sockaddr_in in_remote;
+    int                        addr_len;
+
+    /* Initialize */
+    hostlist = (char **) NULL;
+    *sockp = -1;
+
+    /*
+     * XXX - only know ADDRTYPE_INET.
+     */
+#ifdef KRB5_USE_INET
+    *local = (krb5_address *) malloc(sizeof(krb5_address));
+    *remote = (krb5_address *) malloc(sizeof(krb5_address));
+    if ((*local == NULL) || (*remote == NULL)) {
+       kret = ENOMEM;
+       goto cleanup;
+    }
+    (*local)->addrtype = (*remote)->addrtype = ADDRTYPE_INET;
+    (*local)->length = (*remote)->length = sizeof(struct in_addr);
+    (*local)->contents = (krb5_octet *) malloc(sizeof(struct in_addr));
+    (*remote)->contents = (krb5_octet *) malloc(sizeof(struct in_addr));
+    if (((*local)->contents == NULL) || ((*remote)->contents == NULL)) {
+       kret = ENOMEM;
+       goto cleanup;
+    }
+
+    if ((service = getservbyname(KRB5_ADM_SERVICE_NAME, "tcp")) == NULL) {
+       kret = ENOENT;
+       goto cleanup;
+    }
+    in_remote.sin_port = service->s_port;
+#endif /* KRB5_USE_INET */
+
+    if (kret = krb5_get_krbhst(kcontext, realmp, &hostlist))
+       goto cleanup;
+
+    /* Now count the number of hosts in the realm */
+    count = 0;
+    for (i=0; hostlist[i]; i++)
+      count++;
+    if (count == 0) {
+       kret = ENOENT;  /* something better? */
+       goto cleanup;
+    }
+
+#ifdef KRB5_USE_INET
+    /* Now find a suitable host */
+    for (i=0; hostlist[i]; i++) {
+       remote_host = gethostbyname(hostlist[i]);
+       if (remote_host != (struct hostent *) NULL) {
+           in_remote.sin_family = remote_host->h_addrtype;
+           (void) memcpy((char *) &in_remote.sin_addr,
+                         (char *) remote_host->h_addr,
+                         sizeof(in_remote.sin_addr));
+           break;
+       }
+    }
+
+    /* Open a tcp socket */
+    *sockp = socket(PF_INET, SOCK_STREAM, 0);
+    if (*sockp < 0) {
+       kret = errno;
+       goto cleanup;
+    }
+    else kret = 0;
+
+    if (connect(*sockp,
+               (struct sockaddr *) &in_remote,
+               sizeof(in_remote)) < 0) {
+       kret = errno;
+       goto cleanup;
+    }
+    memcpy((char *) (*remote)->contents,
+          (char *) &in_remote.sin_addr,
+          sizeof(struct in_addr));
+
+    /* Find out local address */
+    addr_len = sizeof(in_local);
+    if (getsockname(*sockp, (struct sockaddr *) &in_local, &addr_len) < 0)
+       kret = errno;
+    else
+       memcpy((char *) (*local)->contents,
+              (char *) &in_local.sin_addr,
+              sizeof(struct in_addr));
+#else  /* KRB5_USE_INET */
+    kret = ENOENT;
+#endif /* KRB5_USE_INET */
+
+ cleanup:
+    if (kret) {
+       if (*sockp >= 0)
+           close(*sockp);
+       if (*local && (*local)->contents)
+           free((*local)->contents);
+       if (*remote && (*remote)->contents)
+           free((*remote)->contents);
+       if (*local) {
+           memset((char *) (*local), 0, sizeof(krb5_address));
+           free(*local);
+           *local = (krb5_address *) NULL;
+       }
+       if (*remote) {
+           memset((char *) (*remote), 0, sizeof(krb5_address));
+           free(*remote);
+           *remote = (krb5_address *) NULL;
+       }
+    }
+    if (hostlist)
+       krb5_xfree(hostlist);
+    return(0);
+}
+\f
+/*
+ * kadm_get_auth()     - Get authorization context.
+ *
+ * Cleanup after success by calling krb5_xfree().
+ * New krb5_auth_context allocated in *ctxp
+ */
+static krb5_error_code
+kadm_get_auth(kcontext, ctxp, local, remote)
+    krb5_context       kcontext;
+    krb5_auth_context  **ctxp;
+    krb5_address       *local;
+    krb5_address       *remote;
+{
+    krb5_auth_con_init(kcontext, ctxp);
+    krb5_auth_con_setflags(kcontext, *ctxp, 
+                          KRB5_AUTH_CONTEXT_RET_SEQUENCE|
+                          KRB5_AUTH_CONTEXT_DO_SEQUENCE);
+    krb5_auth_con_setaddrs(kcontext, *ctxp, local, remote);
+    return(0);
+}
+\f
+/*
+ * krb5_adm_connect()  - Establish the connection to the service.
+ *
+ * Errors are not reported by this routine.
+ * Cleanup after successful invocation must:
+ *     destroy ccache.
+ *     free auth_context
+ *     close socket.
+ */
+krb5_error_code INTERFACE
+krb5_adm_connect(kcontext, user, prompt, opassword, sockp, ctxp, ccachep)
+    krb5_context       kcontext;       /* Context handle       (In ) */
+    char               *user;          /* User specified       (In ) */
+    char               *prompt;        /* Old password prompt  (In ) */
+    char               *opassword;     /* Old Password         (I/O) */
+    int                        *sockp;         /* Socket for conn.     (Out) */
+    krb5_auth_context  **ctxp;         /* Auth context         (Out) */
+    krb5_ccache                *ccachep;       /* Credentials cache    (Out) */
+{
+    krb5_error_code    kret;
+    krb5_principal     client;
+    krb5_creds         creds;
+    krb5_data          server_realm;
+    krb5_data          request_data, suppl_data;
+    krb5_data          response_data;
+    krb5_address       *local_addr;
+    krb5_address       *remote_addr;
+
+    char               *server;
+
+    /* Initialize */
+    memset((char *) &creds, 0, sizeof(krb5_creds));
+    server = (char *) NULL;
+    *sockp = -1;
+    local_addr = remote_addr = (krb5_address *) NULL;
+    client = (krb5_principal) NULL;
+    *ctxp = (krb5_auth_context *) NULL;
+    *ccachep = (krb5_ccache) NULL;
+
+    /*
+     * Find the appropriate credentials cache and set up our identity.
+     */
+    if (kret = kadm_get_ccache(kcontext, user, ccachep, &client))
+       goto cleanup;
+
+    /*
+     * Get initial credentials.
+     */
+    if (kret = kadm_get_creds(kcontext,
+                             *ccachep,
+                             client,
+                             &creds,
+                             prompt,
+                             opassword))
+       goto cleanup;
+
+    /*
+     * Establish connection to server.
+     */
+    if ((server_realm.data = (char *) malloc(client->realm.length+1)) ==
+       (char *) NULL)
+       goto cleanup;
+
+    server_realm.length = client->realm.length;
+    memcpy(server_realm.data, client->realm.data, server_realm.length);
+    server_realm.data[server_realm.length] = '\0';
+    if (kret = kadm_contact_server(kcontext,
+                                  &server_realm,
+                                  sockp,
+                                  &local_addr,
+                                  &remote_addr))
+       goto cleanup;
+
+    /*
+     * Obtain our authorization context
+     */
+    if (kret = kadm_get_auth(kcontext, ctxp, local_addr, remote_addr))
+       goto cleanup;
+
+    /*
+     * Format, then send the KRB_AP_REQ
+     */
+    suppl_data.data = NULL;
+    suppl_data.length = 0;
+    if (kret = krb5_mk_req_extended(kcontext,
+                                   ctxp,
+                                   AP_OPTS_MUTUAL_REQUIRED,
+                                   &suppl_data,
+                                   &creds,
+                                   &request_data))
+       goto cleanup;
+
+    if (kret = krb5_write_message(kcontext, sockp, &request_data))
+       goto cleanup;
+
+    /*
+     * Now read back the response.
+     */
+    if (kret = krb5_read_message(kcontext, sockp, &response_data)) {
+       goto cleanup;
+    }
+    else {
+       krb5_ap_rep_enc_part    *reply = NULL;
+
+       kret = krb5_rd_rep(kcontext, *ctxp, &response_data, &reply);
+       if (reply)
+           krb5_free_ap_rep_enc_part(kcontext, reply);
+    }
+ cleanup:
+    if (server)
+       free(server);
+    if (kret) {
+       if (*ctxp) {
+           krb5_xfree(*ctxp);
+           *ctxp = (krb5_auth_context *) NULL;
+       }
+       if (*sockp >= 0) {
+           close(*sockp);
+           *sockp = -1;
+       }
+       if (local_addr && local_addr->contents)
+           free(local_addr->contents);
+       if (remote_addr && remote_addr->contents)
+           free(remote_addr->contents);
+       if (local_addr)
+           free(local_addr);
+       if (remote_addr)
+           free(remote_addr);
+       if (creds.server)
+           krb5_free_principal(kcontext, creds.server);
+       if (client)
+           krb5_free_principal(kcontext, client);
+       if (*ccachep) {
+           krb5_cc_destroy(kcontext, *ccachep);
+           *ccachep = (krb5_ccache) NULL;
+       }
+    }
+    return(kret);
+
+}
+\f
+/*
+ * krb5_adm_disconnect()       - Disconnect from the administrative service.
+ */
+void INTERFACE
+krb5_adm_disconnect(kcontext, socketp, auth_context, ccache)
+    krb5_context       kcontext;
+    int                        *socketp;
+    krb5_auth_context  *auth_context;
+    krb5_ccache                ccache;
+{
+    if (ccache)
+       krb5_cc_destroy(kcontext, ccache);
+    if (auth_context)
+       krb5_xfree(auth_context);
+    if (*socketp >= 0)
+       close(*socketp);
+}
+
diff --git a/src/lib/kadm/adm_rw.c b/src/lib/kadm/adm_rw.c
new file mode 100644 (file)
index 0000000..ca35be5
--- /dev/null
@@ -0,0 +1,525 @@
+/*
+ * lib/kadm/adm_rw.c
+ *
+ * Copyright 1995 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ *
+ */
+
+/*
+ * Routines to engage in the administrative (password changing) protocol.
+ */
+#define NEED_SOCKETS
+#include "k5-int.h"
+#include "adm_proto.h"
+
+/*
+ * Routines to [de]serialize integers.
+ *
+ * kadm_copyin_int32   - Move a 32-bit integer fron network byte order to
+ *                       host byte order.
+ * kadm_copyout_int32  - Move a 32-bit integer from host byte order to
+ *                       network byte order.
+ */
+static void
+kadm_copyin_int32(cp, ip)
+    char       *cp;
+    krb5_int32 *ip;
+{
+    *ip = (((krb5_int32) ((unsigned char) cp[0]) << 24) +
+          ((krb5_int32) ((unsigned char) cp[1]) << 16) +
+          ((krb5_int32) ((unsigned char) cp[2]) << 8) +
+          ((krb5_int32) ((unsigned char) cp[3])));
+}
+
+static void
+kadm_copyout_int32(outint, cp)
+    krb5_int32 outint;
+    char       *cp;
+{
+    cp[0] = (outint >> 24) & 0xff;
+    cp[1] = (outint >> 16) & 0xff;
+    cp[2] = (outint >> 8) & 0xff;
+    cp[3] = outint & 0xff;
+}
+\f
+/*
+ * krb5_free_adm_data()        - Free data blocks allocated by read_adm... routines.
+ */
+void INTERFACE
+krb5_free_adm_data(kcontext, ncomp, datap)
+    krb5_context       kcontext;
+    krb5_int32         ncomp;
+    krb5_data          *datap;
+{
+    int i;
+    
+    if (datap) {
+       for (i=0; i<ncomp; i++)
+           if (datap[i].data && (datap[i].length > 0))
+               krb5_xfree(datap[i].data);
+
+       krb5_xfree(datap);
+    }
+}
+\f
+/*
+ * krb5_send_adm_cmd() - Send an administrative command.
+ *
+ * Send a list of data in a KRB_PRIV message.  Data takes the format:
+ *     nargs (4 octets in network order)
+ *             arg size 1 (4 octets in network order)
+ *             arg data 1 ("arg size 1" octets)
+ *             .
+ *             .
+ *             .
+ */
+krb5_error_code INTERFACE
+krb5_send_adm_cmd(kcontext, sock, ctx, nargs, arglist)
+    krb5_context       kcontext;       /* Context handle       (In ) */
+    krb5_pointer       sock;           /* Socket to write to   (In ) */
+    krb5_auth_context  *ctx;           /* Auth context         (In ) */
+    int                        nargs;          /* Number of arguments  (In ) */
+    krb5_data          *arglist;       /* Components to write  (In ) */
+{
+    int        writebufsize;
+    int i;
+    char *writebuf;
+    krb5_error_code ret;
+    krb5_int32 ac_flags;
+
+    /*
+     * First check that our auth context has the right flags in it.
+     */
+    if (ret = krb5_auth_con_getflags(kcontext, ctx, &ac_flags))
+       return(ret);
+
+    if ((ac_flags & (KRB5_AUTH_CONTEXT_RET_SEQUENCE|
+                    KRB5_AUTH_CONTEXT_DO_SEQUENCE)) !=
+       (KRB5_AUTH_CONTEXT_RET_SEQUENCE|KRB5_AUTH_CONTEXT_DO_SEQUENCE)) {
+       /* XXX - need a better error */
+       return(KRB5KRB_AP_ERR_MSG_TYPE);
+    }
+
+    ret = 0;
+    /* Calculate write buffer size */
+    writebufsize = sizeof(krb5_int32);
+    for (i=0; i<nargs; i++) {
+       writebufsize += sizeof(krb5_int32);     /* for argument size */
+       writebufsize += arglist[i].length;      /* for actual arg */
+    }
+
+    if (writebuf = (char *) malloc(writebufsize)) {
+       char                    *curr;
+       krb5_data               write_data, out_data;
+       krb5_replay_data        replay_data;
+
+       /* Serialize into write buffer - first number of arguments */
+       curr = writebuf;
+       kadm_copyout_int32(nargs, curr);
+       curr += sizeof(krb5_int32);
+
+       /* Then the arguments */
+       for (i=0; i<nargs; i++) {
+           kadm_copyout_int32(arglist[i].length, curr);
+           curr += sizeof(krb5_int32);
+           memcpy(curr, arglist[i].data, arglist[i].length);
+           curr += arglist[i].length;
+       }
+
+       /* Set up the message */
+       write_data.length = writebufsize;
+       write_data.data = writebuf;
+
+       /* Generate the message */
+       if (!(ret = krb5_mk_priv(kcontext,
+                                ctx,
+                                &write_data,
+                                &out_data,
+                                &replay_data))) {
+           /* Write the message */
+           if (ret = krb5_write_message(kcontext, sock, &out_data))
+               goto cleanup;
+           krb5_xfree(out_data.data);
+       }
+
+    cleanup:
+       /* Paranoia */
+       memset(writebuf, 0, writebufsize);
+       free(writebuf);
+    }
+    else {
+       /* error */
+       ret = ENOMEM;
+    }
+    return(ret);
+}
+\f
+/*
+ * krb5_send_adm_reply()       - Send an administrative reply.
+ *
+ * Send a reply in a KRB_PRIV message.  Data takes the format:
+ *     status (4 octets in network order)
+ *     ncomps (4 octets in network order)
+ *             comp size 1 (4 octets in network order)
+ *             comp data 1 ("comp size 1" octets)
+ *             .
+ *             .
+ *             .
+ */
+krb5_error_code INTERFACE
+krb5_send_adm_reply(kcontext, sock, ctx, cmd_stat, ncomps, complist)
+    krb5_context       kcontext;       /* Context handle       (In ) */
+    krb5_pointer       sock;           /* Socket to write to   (In ) */
+    krb5_auth_context  *ctx;           /* Auth context         (In ) */
+    krb5_int32         cmd_stat;       /* Command status       (In ) */
+    int                        ncomps;         /* Number of arguments  (In ) */
+    krb5_data          *complist;      /* Components to write  (In ) */
+{
+    int        writebufsize;
+    int i;
+    char *writebuf;
+    krb5_error_code ret;
+    krb5_int32 ac_flags;
+
+    /*
+     * First check that our auth context has the right flags in it.
+     */
+    if (ret = krb5_auth_con_getflags(kcontext, ctx, &ac_flags))
+       return(ret);
+
+    if ((ac_flags & (KRB5_AUTH_CONTEXT_RET_SEQUENCE|
+                    KRB5_AUTH_CONTEXT_DO_SEQUENCE)) !=
+       (KRB5_AUTH_CONTEXT_RET_SEQUENCE|KRB5_AUTH_CONTEXT_DO_SEQUENCE)) {
+       /* XXX - need a better error */
+       return(KRB5KRB_AP_ERR_MSG_TYPE);
+    }
+
+    ret = 0;
+    /* Calculate write buffer size */
+    writebufsize = 2 * sizeof(krb5_int32);
+    for (i=0; i<ncomps; i++) {
+       writebufsize += sizeof(krb5_int32);     /* for argument size */
+       writebufsize += complist[i].length;     /* for actual arg */
+    }
+
+    if (writebuf = (char *) malloc(writebufsize)) {
+       char                    *curr;
+       krb5_data               write_data, out_data;
+       krb5_replay_data        replay_data;
+
+       /* Serialize into write buffer - first command status */
+       curr = writebuf;
+       kadm_copyout_int32(cmd_stat, curr);
+       curr += sizeof(krb5_int32);
+
+       /* Now number of reply components */
+       kadm_copyout_int32(ncomps, curr);
+       curr += sizeof(krb5_int32);
+
+       /* Then the arguments */
+       for (i=0; i<ncomps; i++) {
+           kadm_copyout_int32(complist[i].length, curr);
+           curr += sizeof(krb5_int32);
+           memcpy(curr, complist[i].data, complist[i].length);
+           curr += complist[i].length;
+       }
+
+       /* Set up the message */
+       write_data.length = writebufsize;
+       write_data.data = writebuf;
+
+       /* Generate the message */
+       if (!(ret = krb5_mk_priv(kcontext,
+                                ctx,
+                                &write_data,
+                                &out_data,
+                                &replay_data))) {
+           /* Write the message */
+           if (ret = krb5_write_message(kcontext, sock, &out_data))
+               goto cleanup;
+           krb5_xfree(out_data.data);
+       }
+
+    cleanup:
+       /* Paranoia */
+       memset(writebuf, 0, writebufsize);
+       free(writebuf);
+    }
+    else {
+       /* error */
+       ret = ENOMEM;
+    }
+    return(ret);
+}
+\f
+/*
+ * krb5_read_adm_cmd() - Read an administrative protocol command.
+ *
+ * Read an administrative command from the socket.  Expect data in the
+ * same format as send_adm_cmd shoots them out in.
+ *
+ * It is the caller's responsibility to free the memory allocated for
+ * the read in argument list.
+ */
+krb5_error_code INTERFACE
+krb5_read_adm_cmd(kcontext, sock, ctx, nargs, arglist)
+    krb5_context       kcontext;       /* Context handle       (In ) */
+    krb5_pointer       sock;           /* Socket to read from  (In ) */
+    krb5_auth_context  *ctx;           /* Auth context         (In ) */
+    krb5_int32         *nargs;         /* Number of arguments  (Out) */
+    krb5_data          **arglist;      /* List of arguments    (Out) */
+{
+    krb5_data          read_data;
+    krb5_error_code    ret;
+    krb5_data          msg_data;
+    krb5_replay_data   replay_data;
+    krb5_int32 ac_flags;
+
+    /*
+     * First check that our auth context has the right flags in it.
+     */
+    if (ret = krb5_auth_con_getflags(kcontext, ctx, &ac_flags))
+       return(ret);
+
+    if ((ac_flags & (KRB5_AUTH_CONTEXT_RET_SEQUENCE|
+                    KRB5_AUTH_CONTEXT_DO_SEQUENCE)) !=
+       (KRB5_AUTH_CONTEXT_RET_SEQUENCE|KRB5_AUTH_CONTEXT_DO_SEQUENCE)) {
+       /* XXX - need a better error */
+       return(KRB5KRB_AP_ERR_MSG_TYPE);
+    }
+
+    if (!(ret = krb5_read_message(kcontext, sock, &read_data))) {
+       if (!(ret = krb5_rd_priv(kcontext,
+                                ctx,
+                                &read_data,
+                                &msg_data,
+                                &replay_data))) {
+           char *curr;
+           int replyok;
+           int i;
+
+           replyok = 0;
+           /* We'd better have at least one reply component */
+           if (msg_data.length >= sizeof(krb5_int32)) {
+               curr = msg_data.data;
+               kadm_copyin_int32(curr, nargs);
+               curr += sizeof(krb5_int32);
+
+               /* Are there any components to copy? */
+               if (*nargs > 0) {
+
+                   /* Get the memory for the list */
+                   if (*arglist = (krb5_data *)
+                       malloc((size_t) (*nargs) * sizeof(krb5_data))) {
+                       krb5_data *xarglist;
+
+                       xarglist = *arglist;
+                       memset((char *) (xarglist), 0,
+                               (size_t) (*nargs) * sizeof(krb5_data));
+
+                       replyok = 1;
+                       /* Copy out each list entry */
+                       for (i=0; i<*nargs; i++) {
+
+                           /* First get the length of the reply component */
+                           if (curr + sizeof(krb5_int32) - msg_data.data <=
+                               msg_data.length) {
+                               kadm_copyin_int32(curr,
+                                                 &xarglist[i].length);
+                               curr += sizeof(krb5_int32);
+
+                               /* Then get the memory for the actual data */
+                               if ((curr + xarglist[i].length -
+                                    msg_data.data <= msg_data.length) &&
+                                   (xarglist[i].data = (char *)
+                                    malloc(xarglist[i].length+1))) {
+
+                                   /* Then copy it out */
+                                   memcpy(xarglist[i].data,
+                                          curr,
+                                          xarglist[i].length);
+                                   curr += xarglist[i].length;
+
+                                   /* Null terminate for convenience */
+                                   xarglist[i].data[xarglist[i].length] 
+                                           = '\0';
+                               }
+                               else {
+                                   /* Not enough remaining data. */
+                                   replyok = 0;
+                                   break;
+                               }
+                           }
+                           else {
+                               /* Not enough remaining data */
+                               replyok = 0;
+                               break;
+                           }
+                       }
+                       if (!replyok)
+                           krb5_free_adm_data(kcontext, *nargs, *arglist);
+                   }
+               }
+               else {
+                   if (*nargs == 0) {
+                       *arglist = (krb5_data *) NULL;
+                       replyok = 1;
+                   }
+               }
+           }
+           if (!replyok) {
+               ret = KRB5KRB_AP_ERR_MSG_TYPE;  /* syntax error */
+           }
+           memset(msg_data.data, 0, msg_data.length);
+           krb5_xfree(msg_data.data);
+       }
+       krb5_xfree(read_data.data);
+    }
+    return(ret);
+}
+\f
+/*
+ * krb5_read_adm_reply()       - Read an administrative protocol response.
+ *
+ * Expect to read them out in the same format as send_adm_reply shoots them
+ * in.
+ *
+ * It is the caller's responsibility to free the memory allocated for
+ * the read in component list.
+ */
+krb5_error_code INTERFACE
+krb5_read_adm_reply(kcontext, sock, ctx, cmd_stat, ncomps, complist)
+    krb5_context       kcontext;       /* Context handle       (In ) */
+    krb5_pointer       sock;           /* Socket to read from  (In ) */
+    krb5_auth_context  *ctx;           /* Auth context         (In ) */
+    krb5_int32         *cmd_stat;      /* Command status       (Out) */
+    krb5_int32         *ncomps;        /* # of reply components(Out) */
+    krb5_data          **complist;     /* List of components   (Out) */
+{
+    krb5_data          read_data;
+    krb5_error_code    ret;
+    krb5_data          msg_data;
+    krb5_replay_data   replay_data;
+    krb5_int32 ac_flags;
+
+    /*
+     * First check that our auth context has the right flags in it.
+     */
+    if (ret = krb5_auth_con_getflags(kcontext, ctx, &ac_flags))
+       return(ret);
+
+    if ((ac_flags & (KRB5_AUTH_CONTEXT_RET_SEQUENCE|
+                    KRB5_AUTH_CONTEXT_DO_SEQUENCE)) !=
+       (KRB5_AUTH_CONTEXT_RET_SEQUENCE|KRB5_AUTH_CONTEXT_DO_SEQUENCE)) {
+       /* XXX - need a better error */
+       return(KRB5KRB_AP_ERR_MSG_TYPE);
+    }
+
+    if (!(ret = krb5_read_message(kcontext, sock, &read_data))) {
+       if (!(ret = krb5_rd_priv(kcontext,
+                                ctx,
+                                &read_data,
+                                &msg_data,
+                                &replay_data))) {
+           char *curr;
+           int replyok;
+           int i;
+
+           replyok = 0;
+           /* We'd better have at least two reply components */
+           if (msg_data.length >= (2*sizeof(krb5_int32))) {
+               curr = msg_data.data;
+               kadm_copyin_int32(curr, cmd_stat);
+               curr += sizeof(krb5_int32);
+               kadm_copyin_int32(curr, ncomps);
+               curr += sizeof(krb5_int32);
+
+               /* Are there any components to copy? */
+               if (*ncomps > 0) {
+
+                   /* Get the memory for the list */
+                   if (*complist = (krb5_data *)
+                       malloc((size_t) ((*ncomps) * sizeof(krb5_data)))) {
+                       krb5_data *xcomplist;
+
+                       xcomplist = *complist;
+                       memset((char *) (xcomplist), 0, 
+                              (size_t) ((*ncomps) * sizeof(krb5_data)));
+
+                       replyok = 1;
+                       /* Copy out each list entry */
+                       for (i=0; i<*ncomps; i++) {
+
+                           /* First get the length of the reply component */
+                           if (curr + sizeof(krb5_int32) - msg_data.data <=
+                               msg_data.length) {
+                               kadm_copyin_int32(curr,
+                                                 &xcomplist[i].length);
+                               curr += sizeof(krb5_int32);
+
+                               /* Then get the memory for the actual data */
+                               if ((curr + xcomplist[i].length -
+                                    msg_data.data <= msg_data.length) &&
+                                   (xcomplist[i].data = (char *)
+                                    malloc(xcomplist[i].length+1))) {
+
+                                   /* Then copy it out */
+                                   memcpy(xcomplist[i].data,
+                                          curr,
+                                          xcomplist[i].length);
+                                   curr += xcomplist[i].length;
+
+                                   /* Null terminate for convenience */
+                                   xcomplist[i].data[xcomplist[i].length] 
+                                           = '\0';
+                               }
+                               else {
+                                   /* Not enough remaining data. */
+                                   replyok = 0;
+                                   break;
+                               }
+                           }
+                           else {
+                               /* Not enough remaining data */
+                               replyok = 0;
+                               break;
+                           }
+                       }
+                       if (!replyok)
+                           krb5_free_adm_data(kcontext, *ncomps, *complist);
+                   }
+               }
+               else {
+                   if (*ncomps == 0) {
+                       *complist = (krb5_data *) NULL;
+                       replyok = 1;
+                   }
+               }
+           }
+           if (!replyok) {
+               ret = KRB5KRB_AP_ERR_MSG_TYPE;  /* syntax error */
+           }
+           memset(msg_data.data, 0, msg_data.length);
+           krb5_xfree(msg_data.data);
+       }
+       krb5_xfree(read_data.data);
+    }
+    return(ret);
+}
diff --git a/src/lib/kadm/configure.in b/src/lib/kadm/configure.in
new file mode 100644 (file)
index 0000000..e15eed3
--- /dev/null
@@ -0,0 +1,13 @@
+AC_INIT(configure.in)
+WITH_CCOPTS
+CONFIG_RULES
+AC_SET_BUILDTOP
+AC_PROG_ARCHIVE
+AC_PROG_ARCHIVE_ADD
+AC_PROG_RANLIB
+AC_PROG_INSTALL
+AC_HAVE_HEADERS(pwd.h)
+AppendRule([all:: libkadm.a])
+KRB_INCLUDE
+WITH_KRB5ROOT
+V5_AC_OUTPUT_MAKEFILE