6 * Copyright (C) 1987 by the Massachusetts Institute of Technology
8 * Utility functions for communication with the Kerberos admin_server
10 * Original version written by Jeffery I. Schiller, January 1987
11 * Completely gutted and rewritten by Bill Sommerfeld, August 1987
14 * Revision 1.5 1988-08-01 00:42:54 qjb
15 * Changed names of kerberos calls to new names from old ones.
17 * Revision 1.4 87/09/09 14:59:06 wesommer
18 * Allocate a new socket each time rather than keeping one around,
19 * due to protocol problems.
21 * Revision 1.3 87/09/04 22:30:34 wesommer
22 * Un-crock the KDC host (oops -- this one got distributed!!).
24 * Revision 1.2 87/08/22 17:13:59 wesommer
25 * Make admin_errmsg external rather than static.
28 * Revision 1.1 87/08/07 13:50:37 wesommer
34 static char *rcsid_admin_call_c = "$Header$";
37 #include <sys/errno.h>
38 #include <sys/types.h>
40 #include <sys/ioctl.h>
41 #include <sys/socket.h>
42 #include <netinet/in.h>
48 #include "admin_err.h"
49 #include "admin_server.h"
53 extern int krb_err_base; /* Offset between com_err and kerberos codes */
54 extern int errno; /* System call error numbers */
56 extern long gethostid();
58 static u_char *strapp(); /* string append function */
60 static int inited = 0; /* are we initialized? */
61 static int admin_fd = -1; /* socket to talk to admin_server. */
63 static struct sockaddr_in admin_addr; /* address of admin server. */
64 static struct sockaddr_in my_addr; /* address bound to admin_fd. */
65 static int my_addr_len; /* size of above address. */
67 static char krbrlm[REALM_SZ]; /* Local kerberos realm */
68 char admin_errmsg[BUFSIZ]; /* Server error message */
71 * Initialize socket, etc. to use to talk to admin_server.
76 register int status = 0;
79 struct hostent *hp; /* host to talk to */
80 struct servent *sp; /* service to talk to */
83 if (status = get_krbrlm(krbrlm, 1)) {
84 status += krb_err_base;
92 hp = gethostbyname(KERB_HOST);
94 status = ADMIN_UNKNOWN_HOST;
97 sp = getservbyname("atest3", "udp");
99 status = ADMIN_UNKNOWN_SERVICE;
102 bzero((char *)&admin_addr, sizeof(admin_addr));
103 admin_addr.sin_family = hp->h_addrtype;
104 bcopy((char *)hp->h_addr, (char *)&admin_addr.sin_addr, hp->h_length);
105 admin_addr.sin_port = sp->s_port;
112 (void) close(admin_fd);
118 * Build and transmit request to admin_server, and wait for
119 * response from server. Returns a standard error code.
123 admin_call(opcode, pname, old_passwd, new_passwd, crypt_passwd)
131 register u_char *bp; /* Pointer into buffer. */
132 u_char *ep; /* End of buffer pointer. */
134 u_char pvt_buf[BUFSIZ]; /* private message plaintext */
135 int pvt_len; /* length of valid data in pvt_buf */
137 u_char sealed_buf[BUFSIZ]; /* sealed version of private message */
138 int sealed_len; /* length of valid data in sealed_buf */
140 u_long checksum; /* quad_cksum of sealed request. */
142 C_Block sess_key; /* Session key. */
143 Key_schedule sess_sched; /* Key schedule for above. */
145 CREDENTIALS cred; /* Kerberos credentials. */
146 KTEXT_ST authent; /* Authenticator */
147 KTEXT_ST reply; /* Reply from admin_server */
148 MSG_DAT msg_data; /* Decrypted message */
149 int attempts; /* Number of retransmits so far */
151 struct sockaddr rec_addr; /* Address we got reply from */
152 int rec_addr_len; /* Length of that address */
153 int on = 1; /* ioctl argument */
157 status = admin_call_init();
158 if (status) goto bad;
164 * sealed message consists of:
165 * version number (one byte).
166 * request code (one byte).
167 * principal name (null terminated).
168 * new password (in the clear, null terminated).
169 * old password or instance (null terminated)
170 * crypt(new password, seed) (null terminated).
172 * a '\001' character.
173 * This is all sealed inside a private_message, with an
174 * authenticator tacked on in front.
178 ep = pvt_buf + sizeof(pvt_buf);
180 *bp++ = PW_SRV_VERSION;
182 if ((bp = strapp(bp, (u_char *)pname, ep)) &&
183 (bp = strapp(bp, (u_char *)new_passwd, ep)) &&
184 (bp = strapp(bp, (u_char *)old_passwd, ep)) &&
185 (bp = strapp(bp, (u_char *)crypt_passwd, ep))) {
188 pvt_len = bp - pvt_buf;
190 status = ADMIN_TOO_LONG;
195 * find our session key.
198 if (status = krb_get_cred("changepw", "kerberos", krbrlm, &cred)) {
199 status += krb_err_base;
203 bcopy((char *)cred.session, (char *)sess_key, sizeof(sess_key));
204 bzero((char *)cred.session, sizeof(sess_key)); /* lest we remember */
206 if(key_sched(sess_key, sess_sched)) {
207 status = ADMIN_BAD_KEY;
215 admin_fd = socket(admin_addr.sin_family, SOCK_DGRAM, 0);
221 bzero((char *)&my_addr, sizeof(my_addr));
223 my_addr.sin_family = admin_addr.sin_family;
224 my_addr.sin_addr.s_addr = gethostid();
226 if (bind(admin_fd, &my_addr, sizeof(my_addr)) < 0) {
231 my_addr_len = sizeof(my_addr);
233 if (getsockname(admin_fd, (struct sockaddr *)&my_addr,
239 if (ioctl(admin_fd, FIONBIO, (char *)&on) < 0) {
245 * Encrypt the message using the session key.
246 * Since this contains passwords, it must be kept from prying eyes.
249 sealed_len = krb_mk_priv(pvt_buf, sealed_buf, pvt_len,
250 sess_sched, sess_key, &my_addr,
252 if (sealed_len < 0) {
253 status = ADMIN_CANT_ENCRYPT;
258 * Checksum the cypher text, to guard against tampering in flight.
261 checksum = quad_cksum(sealed_buf, NULL, sealed_len, 0, sess_key);
264 * Make an authenticator, so the server can learn the session key
265 * and know who we are.
268 if (status = krb_mk_req(&authent, "changepw", "kerberos", krbrlm,
270 status += krb_err_base;
275 * Add the sealed message to the end of the authenticator.
278 if ((authent.length + sealed_len) > MAX_KTXT_LEN) {
279 status = ADMIN_TOO_LONG;
283 bcopy(sealed_buf, authent.dat + authent.length, sealed_len);
284 authent.length += sealed_len;
287 * send it off, and wait for a reply.
292 while (attempts++ < RETRY_LIMIT) {
293 int active; /* number of active fds (from select). */
294 fd_set set; /* interesting fd's. */
295 struct timeval timeout; /* timeout on select. */
297 if (sendto(admin_fd, (char *)authent.dat, authent.length,
298 0, &admin_addr, sizeof(admin_addr)) != authent.length) {
304 FD_SET(admin_fd, &set);
306 timeout.tv_sec = USER_TIMEOUT;
309 active = select(admin_fd+1, &set, (fd_set *)0, (fd_set *)0, &timeout);
313 } else if (active == 0) continue;
315 reply.length = recvfrom(admin_fd, (char *)reply.dat,
316 sizeof(reply.dat), 0,
317 &rec_addr, &rec_addr_len);
318 if (reply.length < 0) continue;
320 if (!bcmp(&rec_addr, &admin_addr, sizeof(admin_addr)))
321 /* the spoofers are out in force.. */
324 break; /* got one.. */
328 if (attempts > RETRY_LIMIT) {
333 if (pkt_version((&reply)) != KRB_PROT_VERSION) {
334 status = ADMIN_BAD_VERSION;
338 if ((pkt_msg_type((&reply)) & ~1) != AUTH_MSG_PRIVATE) {
340 if (*bp++ != KRB_PROT_VERSION) {
341 status = ADMIN_BAD_VERSION;
344 if (*bp++ != AUTH_MSG_ERR_REPLY) {
345 status = ADMIN_UNKNOWN_CODE;
348 bp += strlen((char *)bp) + 1; /* Skip name */
349 bp += strlen((char *)bp) + 1; /* Skip instance */
350 bp += strlen((char *)bp) + 1; /* Skip realm */
352 /* null-terminate error string */
353 reply.dat[reply.length] = '\0';
356 status = ADMIN_SERVER_ERROR;
357 strcpy(admin_errmsg, bp);
360 status = ADMIN_UNKNOWN_CODE;
364 status = krb_rd_priv(reply.dat, reply.length,
365 sess_sched, sess_key,
366 &admin_addr, &my_addr,
369 status += krb_err_base;
372 bp = msg_data.app_data;
374 if (*bp++ != PW_SRV_VERSION) {
375 status = ADMIN_BAD_VERSION;
378 if (*bp++ != INSTALL_REPLY) {
379 status = ADMIN_UNKNOWN_CODE;
384 /* fall through into cleanup */
388 * Paranoia: shred all the incriminating evidence.
389 * We'll let the caller shred the arguments.
391 bzero((char *)sess_key, sizeof(sess_key));
392 bzero((char *)sess_sched, sizeof(sess_sched));
393 bzero(pvt_buf, sizeof(pvt_buf));
396 (void) close(admin_fd);
404 * Copies characters from source to dest, returning a pointer to the
405 * point in dest after the last character copied from source.
406 * If this would be greater than end, no characters are copied, and NULL
407 * is returned instead.
410 static u_char *strapp(dest, source, end)
411 register u_char *dest, *source, *end;
413 register int length = strlen(source) + 1;
414 if (dest+length > end) return NULL;
416 bcopy(source, dest, length);
417 return dest + length;
425 * c-continued-statement-offset: 4
427 * c-argdecl-indent: 4