]> andersk Git - moira.git/blobdiff - reg_svr/admin_call.c
Initial revision
[moira.git] / reg_svr / admin_call.c
diff --git a/reg_svr/admin_call.c b/reg_svr/admin_call.c
new file mode 100644 (file)
index 0000000..67d01d3
--- /dev/null
@@ -0,0 +1,409 @@
+/*
+ *     $Source$
+ *     $Author$
+ *     $Header$
+ *
+ *     Copyright (C) 1987 by the Massachusetts Institute of Technology
+ *
+ *     Utility functions for communication with the Kerberos admin_server
+ *
+ *     Original version written by Jeffery I. Schiller, January 1987
+ *     Completely gutted and rewritten by Bill Sommerfeld, August 1987
+ *
+ *     $Log$
+ *     Revision 1.1  1987-08-07 13:50:37  wesommer
+ *     Initial revision
+ *
+ */
+
+#ifndef lint
+static char *rcsid_admin_call_c = "$Header$";
+#endif lint
+
+#include <sys/errno.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include <netdb.h>
+#include <strings.h>
+#include <stdio.h>
+
+#include "admin_err.h"
+#include "admin_server.h"
+#include "prot.h"
+#include "krb.h"
+
+extern int krb_err_base;       /* Offset between com_err and kerberos codes */
+extern int errno;              /* System call error numbers */
+
+extern long gethostid();
+
+static u_char *strapp();               /* string append function */
+
+static int inited = 0;         /* are we initialized? */
+static int admin_fd = -1;      /* socket to talk to admin_server. */
+
+static struct sockaddr_in admin_addr; /* address of admin server. */
+static struct sockaddr_in my_addr;    /* address bound to admin_fd. */
+static int my_addr_len;                /* size of above address. */
+
+static char krbrlm[REALM_SZ];  /* Local kerberos realm */
+static char admin_errmsg[BUFSIZ]; /* Server error message */
+
+/*
+ * Initialize socket, etc. to use to talk to admin_server.
+ */
+
+int admin_call_init()
+{
+    register int status = 0;
+    
+    if (!inited) {
+       struct hostent *hp;     /* host to talk to */
+       struct servent *sp;     /* service to talk to */
+       int on = 1;             /* ioctl argument */
+       
+       init_kadm_err_tbl();
+       if (status = get_krbrlm(krbrlm, 1)) {
+           status += krb_err_base;
+           goto punt;
+       }
+
+       /*
+        * Locate server.
+        */
+
+       hp = gethostbyname(KERB_HOST);
+       if (!hp) {
+           status = ADMIN_UNKNOWN_HOST;
+           goto punt;
+       }
+       sp = getservbyname("atest3", "udp");
+       if (!sp) {
+           status = ADMIN_UNKNOWN_SERVICE;
+           goto punt;
+       }
+       bzero((char *)&admin_addr, sizeof(admin_addr));
+       admin_addr.sin_family = hp->h_addrtype;
+       bcopy((char *)hp->h_addr, (char *)&admin_addr.sin_addr, hp->h_length);
+       admin_addr.sin_port = sp->s_port;
+
+       /*
+        * Set up socket.
+        */
+
+       admin_fd = socket(hp->h_addrtype, SOCK_DGRAM, 0);
+       if (admin_fd < 0) {
+           status = errno;
+           goto punt;
+       }
+
+       bzero((char *)&my_addr, sizeof(my_addr));
+
+       my_addr.sin_family = admin_addr.sin_family;
+       my_addr.sin_addr.s_addr = gethostid();
+
+       if (bind(admin_fd, &my_addr, sizeof(my_addr)) < 0) {
+           status = errno;
+           goto punt;
+       }
+
+       my_addr_len = sizeof(my_addr);
+
+       if (getsockname(admin_fd, (struct sockaddr *)&my_addr,
+                       &my_addr_len) < 0) {
+           status = errno;
+           goto punt;
+       }
+
+       if (ioctl(admin_fd, FIONBIO, (char *)&on) < 0) {
+           status = errno;
+           goto punt;
+       }
+       
+       inited = 1;
+    }
+    return 0;
+    
+punt:
+    (void) close(admin_fd);
+    admin_fd = -1;
+    return status;
+}
+
+/*
+ * Build and transmit request to admin_server, and wait for
+ * response from server.  Returns a standard error code.
+ */
+
+int
+admin_call(opcode, pname, old_passwd, new_passwd, crypt_passwd)
+    int opcode;
+    char *pname;
+    char *old_passwd;
+    char *new_passwd;
+    char *crypt_passwd;
+{
+    int status;
+    register u_char *bp;       /* Pointer into buffer. */
+    u_char *ep;                        /* End of buffer pointer. */
+    
+    u_char pvt_buf[BUFSIZ];    /* private message plaintext */
+    int pvt_len;               /* length of valid data in pvt_buf */
+
+    u_char sealed_buf[BUFSIZ]; /* sealed version of private message */
+    int sealed_len;            /* length of valid data in sealed_buf */
+    
+    u_long checksum;           /* quad_cksum of sealed request. */
+    
+    C_Block sess_key;          /* Session key. */
+    Key_schedule sess_sched;   /* Key schedule for above. */
+    
+    CREDENTIALS cred;          /* Kerberos credentials. */
+    KTEXT_ST authent;          /* Authenticator */
+    KTEXT_ST reply;            /* Reply from admin_server */
+    MSG_DAT msg_data;          /* Decrypted message */
+    int attempts;              /* Number of retransmits so far */
+
+    struct sockaddr rec_addr;  /* Address we got reply from */
+    int rec_addr_len;          /* Length of that address */
+    
+    if (!inited) {
+       status = admin_call_init();
+       if (status) goto bad;
+    }
+
+    /*
+     * assemble packet:
+     *
+     * sealed message consists of:
+     * version number (one byte).
+     * request code (one byte).
+     * principal name (null terminated).
+     * new password (in the clear, null terminated).
+     * old password or instance (null terminated)
+     * crypt(new password, seed) (null terminated).
+     * an extra null.
+     * a '\001' character.
+     * This is all sealed inside a private_message, with an
+     * authenticator tacked on in front.
+     */
+
+    bp = pvt_buf;
+    ep = pvt_buf + sizeof(pvt_buf);
+    
+    *bp++ = PW_SRV_VERSION;
+    *bp++ = opcode;
+    if ((bp = strapp(bp, (u_char *)pname, ep)) &&
+       (bp = strapp(bp, (u_char *)new_passwd, ep)) &&
+       (bp = strapp(bp, (u_char *)old_passwd, ep)) &&
+       (bp = strapp(bp, (u_char *)crypt_passwd, ep))) {
+       *bp++ = '\0';
+       *bp++ = '\1';
+       pvt_len = bp - pvt_buf;
+    } else {
+       status = ADMIN_TOO_LONG;
+       goto bad;
+    }
+    
+    /*
+     * find our session key.
+     */
+
+    if (status = get_credentials("changepw", "kerberos", krbrlm, &cred)) {
+       status += krb_err_base;
+       goto bad;
+    }
+
+    bcopy((char *)cred.session, (char *)sess_key, sizeof(sess_key));
+    bzero((char *)cred.session, sizeof(sess_key)); /* lest we remember */
+
+    if(key_sched(sess_key, sess_sched)) {
+       status = ADMIN_BAD_KEY;
+       goto bad;
+    }
+
+    /*
+     * Encrypt the message using the session key.
+     * Since this contains passwords, it must be kept from prying eyes.
+     */
+
+    sealed_len = mk_private_msg(pvt_buf, sealed_buf, pvt_len,
+                                   sess_sched, sess_key, &my_addr,
+                                   &admin_addr);
+    if (sealed_len < 0) {
+       status = ADMIN_CANT_ENCRYPT;
+       goto bad;
+    }
+
+    /*
+     * Checksum the cypher text, to guard against tampering in flight.
+     */
+
+    checksum = quad_cksum(sealed_buf, NULL, sealed_len, 0, sess_key);
+    
+    /*
+     * Make an authenticator, so the server can learn the session key
+     * and know who we are.
+     */
+
+    if (status = mk_ap_req(&authent, "changepw", "kerberos", krbrlm,
+                          checksum)) {
+       status += krb_err_base;
+       goto bad;
+    }
+
+    /*
+     * Add the sealed message to the end of the authenticator.
+     */
+
+    if ((authent.length + sealed_len) > MAX_KTXT_LEN) {
+       status = ADMIN_TOO_LONG;
+       goto bad;
+    }
+
+    bcopy(sealed_buf, authent.dat + authent.length, sealed_len);
+    authent.length += sealed_len;
+
+    /*
+     * send it off, and wait for a reply.
+     */
+
+    attempts = 0;
+    
+    while (attempts++ < RETRY_LIMIT) {
+       int active;             /* number of active fds (from select). */
+       fd_set set;             /* interesting fd's. */
+       struct timeval timeout; /* timeout on select. */
+       
+       if (sendto(admin_fd, (char *)authent.dat, authent.length,
+                  0, &admin_addr, sizeof(admin_addr)) != authent.length) {
+           status = errno;
+           goto bad;
+       }
+
+       FD_ZERO(&set);
+       FD_SET(admin_fd, &set);
+
+       timeout.tv_sec = USER_TIMEOUT;
+       timeout.tv_usec = 0;
+           
+       active = select(admin_fd+1, &set, (fd_set *)0, (fd_set *)0, &timeout);
+       if (active < 0) {
+           status = errno;
+           goto bad;
+       } else if (active == 0) continue;
+       else {
+           reply.length = recvfrom(admin_fd, (char *)reply.dat,
+                                   sizeof(reply.dat), 0,
+                                   &rec_addr, &rec_addr_len);
+           if (reply.length < 0) continue;
+#ifdef notdef
+           if (!bcmp(&rec_addr, &admin_addr, sizeof(admin_addr)))
+               /* the spoofers are out in force.. */
+               continue;
+#endif notdef
+           break;              /* got one.. */
+       }
+    }
+
+    if (attempts > RETRY_LIMIT) {
+       status = ETIMEDOUT;
+       goto bad;
+    }
+
+    if (pkt_version((&reply)) != KRB_PROT_VERSION) {
+       status = ADMIN_BAD_VERSION;
+       goto bad;
+    }
+
+    if ((pkt_msg_type((&reply)) & ~1) != AUTH_MSG_PRIVATE) {
+       bp = reply.dat;
+       if (*bp++ != KRB_PROT_VERSION) {
+           status = ADMIN_BAD_VERSION;
+           goto bad;
+       }
+       if (*bp++ != AUTH_MSG_ERR_REPLY) {
+           status = ADMIN_UNKNOWN_CODE;
+           goto bad;
+       }
+       bp += strlen((char *)bp) + 1;   /* Skip name */
+       bp += strlen((char *)bp) + 1;   /* Skip instance */
+       bp += strlen((char *)bp) + 1;   /* Skip realm */
+
+       /* null-terminate error string */
+       reply.dat[reply.length] = '\0'; 
+
+       if (*bp++ == 126) {
+           status = ADMIN_SERVER_ERROR;
+           strcpy(admin_errmsg, bp);
+           goto bad;
+       } else {
+           status = ADMIN_UNKNOWN_CODE;
+           goto bad;
+       }
+    }
+    status = rd_private_msg(reply.dat, reply.length,
+                           sess_sched, sess_key,
+                           &admin_addr, &my_addr,
+                           &msg_data);
+    if (status) {
+       status += krb_err_base;
+       goto bad;
+    }
+    bp = msg_data.app_data;
+
+    if (*bp++ != PW_SRV_VERSION) {
+       status = ADMIN_BAD_VERSION;
+       goto bad;
+    }
+    if (*bp++ != INSTALL_REPLY) {
+       status = ADMIN_UNKNOWN_CODE;
+       goto bad;
+    }
+
+    status = 0;
+    /* fall through into cleanup */
+
+bad:
+    /*
+     * Paranoia: shred all the incriminating evidence.
+     * We'll let the caller shred the arguments.
+     */
+    bzero((char *)sess_key, sizeof(sess_key));
+    bzero((char *)sess_sched, sizeof(sess_sched));
+    bzero(pvt_buf, sizeof(pvt_buf));
+    return status;
+}
+
+/*
+ * Copies characters from source to dest, returning a pointer to the
+ * point in dest after the last character copied from source.
+ * If this would be greater than end, no characters are copied, and NULL
+ * is returned instead.
+ */
+
+static u_char *strapp(dest, source, end)
+    register u_char *dest, *source, *end;
+{
+    register int length = strlen(source) + 1;
+    if (dest+length > end) return NULL;
+    else {
+       bcopy(source, dest, length);
+       return dest + length;
+    }
+}
+
+/*
+ * Local Variables:
+ * mode: c
+ * c-indent-level: 4
+ * c-continued-statement-offset: 4
+ * c-brace-offset: -4
+ * c-argdecl-indent: 4
+ * c-label-offset: -4
+ * End:
+ */
This page took 0.045915 seconds and 4 git commands to generate.