--- /dev/null
+/*
+ * 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);
+}
+
--- /dev/null
+/*
+ * 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);
+}