]> andersk Git - moira.git/blob - reg_svr/admin_call.c
Un-crock the KDC host (oops -- this one got distributed!!).
[moira.git] / reg_svr / admin_call.c
1 /*
2  *      $Source$
3  *      $Author$
4  *      $Header$
5  *
6  *      Copyright (C) 1987 by the Massachusetts Institute of Technology
7  *
8  *      Utility functions for communication with the Kerberos admin_server
9  *
10  *      Original version written by Jeffery I. Schiller, January 1987
11  *      Completely gutted and rewritten by Bill Sommerfeld, August 1987
12  *
13  *      $Log$
14  *      Revision 1.3  1987-09-04 22:30:34  wesommer
15  *      Un-crock the KDC host (oops -- this one got distributed!!).
16  *
17  * Revision 1.2  87/08/22  17:13:59  wesommer
18  * Make admin_errmsg external rather than static.
19  * Crock up KDC host.
20  * 
21  * Revision 1.1  87/08/07  13:50:37  wesommer
22  * Initial revision
23  * 
24  */
25
26 #ifndef lint
27 static char *rcsid_admin_call_c = "$Header$";
28 #endif lint
29
30 #include <sys/errno.h>
31 #include <sys/types.h>
32 #include <sys/time.h>
33 #include <sys/ioctl.h>
34 #include <sys/socket.h>
35 #include <netinet/in.h>
36
37 #include <netdb.h>
38 #include <strings.h>
39 #include <stdio.h>
40
41 #include "admin_err.h"
42 #include "admin_server.h"
43 #include "prot.h"
44 #include "krb.h"
45
46 extern int krb_err_base;        /* Offset between com_err and kerberos codes */
47 extern int errno;               /* System call error numbers */
48
49 extern long gethostid();
50
51 static u_char *strapp();                /* string append function */
52
53 static int inited = 0;          /* are we initialized? */
54 static int admin_fd = -1;       /* socket to talk to admin_server. */
55
56 static struct sockaddr_in admin_addr; /* address of admin server. */
57 static struct sockaddr_in my_addr;    /* address bound to admin_fd. */
58 static int my_addr_len;         /* size of above address. */
59
60 static char krbrlm[REALM_SZ];   /* Local kerberos realm */
61 char admin_errmsg[BUFSIZ]; /* Server error message */
62
63 /*
64  * Initialize socket, etc. to use to talk to admin_server.
65  */
66
67 int admin_call_init()
68 {
69     register int status = 0;
70     
71     if (!inited) {
72         struct hostent *hp;     /* host to talk to */
73         struct servent *sp;     /* service to talk to */
74         int on = 1;             /* ioctl argument */
75         
76         init_kadm_err_tbl();
77         if (status = get_krbrlm(krbrlm, 1)) {
78             status += krb_err_base;
79             goto punt;
80         }
81
82         /*
83          * Locate server.
84          */
85
86         hp = gethostbyname(KERB_HOST);
87         if (!hp) {
88             status = ADMIN_UNKNOWN_HOST;
89             goto punt;
90         }
91         sp = getservbyname("atest3", "udp");
92         if (!sp) {
93             status = ADMIN_UNKNOWN_SERVICE;
94             goto punt;
95         }
96         bzero((char *)&admin_addr, sizeof(admin_addr));
97         admin_addr.sin_family = hp->h_addrtype;
98         bcopy((char *)hp->h_addr, (char *)&admin_addr.sin_addr, hp->h_length);
99         admin_addr.sin_port = sp->s_port;
100
101         /*
102          * Set up socket.
103          */
104
105         admin_fd = socket(hp->h_addrtype, SOCK_DGRAM, 0);
106         if (admin_fd < 0) {
107             status = errno;
108             goto punt;
109         }
110
111         bzero((char *)&my_addr, sizeof(my_addr));
112
113         my_addr.sin_family = admin_addr.sin_family;
114         my_addr.sin_addr.s_addr = gethostid();
115
116         if (bind(admin_fd, &my_addr, sizeof(my_addr)) < 0) {
117             status = errno;
118             goto punt;
119         }
120
121         my_addr_len = sizeof(my_addr);
122
123         if (getsockname(admin_fd, (struct sockaddr *)&my_addr,
124                         &my_addr_len) < 0) {
125             status = errno;
126             goto punt;
127         }
128
129         if (ioctl(admin_fd, FIONBIO, (char *)&on) < 0) {
130             status = errno;
131             goto punt;
132         }
133         
134         inited = 1;
135     }
136     return 0;
137     
138 punt:
139     (void) close(admin_fd);
140     admin_fd = -1;
141     return status;
142 }
143
144 /*
145  * Build and transmit request to admin_server, and wait for
146  * response from server.  Returns a standard error code.
147  */
148
149 int
150 admin_call(opcode, pname, old_passwd, new_passwd, crypt_passwd)
151     int opcode;
152     char *pname;
153     char *old_passwd;
154     char *new_passwd;
155     char *crypt_passwd;
156 {
157     int status;
158     register u_char *bp;        /* Pointer into buffer. */
159     u_char *ep;                 /* End of buffer pointer. */
160     
161     u_char pvt_buf[BUFSIZ];     /* private message plaintext */
162     int pvt_len;                /* length of valid data in pvt_buf */
163
164     u_char sealed_buf[BUFSIZ];  /* sealed version of private message */
165     int sealed_len;             /* length of valid data in sealed_buf */
166     
167     u_long checksum;            /* quad_cksum of sealed request. */
168     
169     C_Block sess_key;           /* Session key. */
170     Key_schedule sess_sched;    /* Key schedule for above. */
171     
172     CREDENTIALS cred;           /* Kerberos credentials. */
173     KTEXT_ST authent;           /* Authenticator */
174     KTEXT_ST reply;             /* Reply from admin_server */
175     MSG_DAT msg_data;           /* Decrypted message */
176     int attempts;               /* Number of retransmits so far */
177
178     struct sockaddr rec_addr;   /* Address we got reply from */
179     int rec_addr_len;           /* Length of that address */
180     
181     if (!inited) {
182         status = admin_call_init();
183         if (status) goto bad;
184     }
185
186     /*
187      * assemble packet:
188      *
189      * sealed message consists of:
190      * version number (one byte).
191      * request code (one byte).
192      * principal name (null terminated).
193      * new password (in the clear, null terminated).
194      * old password or instance (null terminated)
195      * crypt(new password, seed) (null terminated).
196      * an extra null.
197      * a '\001' character.
198      * This is all sealed inside a private_message, with an
199      * authenticator tacked on in front.
200      */
201
202     bp = pvt_buf;
203     ep = pvt_buf + sizeof(pvt_buf);
204     
205     *bp++ = PW_SRV_VERSION;
206     *bp++ = opcode;
207     if ((bp = strapp(bp, (u_char *)pname, ep)) &&
208         (bp = strapp(bp, (u_char *)new_passwd, ep)) &&
209         (bp = strapp(bp, (u_char *)old_passwd, ep)) &&
210         (bp = strapp(bp, (u_char *)crypt_passwd, ep))) {
211         *bp++ = '\0';
212         *bp++ = '\1';
213         pvt_len = bp - pvt_buf;
214     } else {
215         status = ADMIN_TOO_LONG;
216         goto bad;
217     }
218     
219     /*
220      * find our session key.
221      */
222
223     if (status = get_credentials("changepw", "kerberos", krbrlm, &cred)) {
224         status += krb_err_base;
225         goto bad;
226     }
227
228     bcopy((char *)cred.session, (char *)sess_key, sizeof(sess_key));
229     bzero((char *)cred.session, sizeof(sess_key)); /* lest we remember */
230
231     if(key_sched(sess_key, sess_sched)) {
232         status = ADMIN_BAD_KEY;
233         goto bad;
234     }
235
236     /*
237      * Encrypt the message using the session key.
238      * Since this contains passwords, it must be kept from prying eyes.
239      */
240
241     sealed_len = mk_private_msg(pvt_buf, sealed_buf, pvt_len,
242                                     sess_sched, sess_key, &my_addr,
243                                     &admin_addr);
244     if (sealed_len < 0) {
245         status = ADMIN_CANT_ENCRYPT;
246         goto bad;
247     }
248
249     /*
250      * Checksum the cypher text, to guard against tampering in flight.
251      */
252
253     checksum = quad_cksum(sealed_buf, NULL, sealed_len, 0, sess_key);
254     
255     /*
256      * Make an authenticator, so the server can learn the session key
257      * and know who we are.
258      */
259
260     if (status = mk_ap_req(&authent, "changepw", "kerberos", krbrlm,
261                            checksum)) {
262         status += krb_err_base;
263         goto bad;
264     }
265
266     /*
267      * Add the sealed message to the end of the authenticator.
268      */
269
270     if ((authent.length + sealed_len) > MAX_KTXT_LEN) {
271         status = ADMIN_TOO_LONG;
272         goto bad;
273     }
274
275     bcopy(sealed_buf, authent.dat + authent.length, sealed_len);
276     authent.length += sealed_len;
277
278     /*
279      * send it off, and wait for a reply.
280      */
281
282     attempts = 0;
283     
284     while (attempts++ < RETRY_LIMIT) {
285         int active;             /* number of active fds (from select). */
286         fd_set set;             /* interesting fd's. */
287         struct timeval timeout; /* timeout on select. */
288         
289         if (sendto(admin_fd, (char *)authent.dat, authent.length,
290                    0, &admin_addr, sizeof(admin_addr)) != authent.length) {
291             status = errno;
292             goto bad;
293         }
294
295         FD_ZERO(&set);
296         FD_SET(admin_fd, &set);
297
298         timeout.tv_sec = USER_TIMEOUT;
299         timeout.tv_usec = 0;
300             
301         active = select(admin_fd+1, &set, (fd_set *)0, (fd_set *)0, &timeout);
302         if (active < 0) {
303             status = errno;
304             goto bad;
305         } else if (active == 0) continue;
306         else {
307             reply.length = recvfrom(admin_fd, (char *)reply.dat,
308                                     sizeof(reply.dat), 0,
309                                     &rec_addr, &rec_addr_len);
310             if (reply.length < 0) continue;
311 #ifdef notdef
312             if (!bcmp(&rec_addr, &admin_addr, sizeof(admin_addr)))
313                 /* the spoofers are out in force.. */
314                 continue;
315 #endif notdef
316             break;              /* got one.. */
317         }
318     }
319
320     if (attempts > RETRY_LIMIT) {
321         status = ETIMEDOUT;
322         goto bad;
323     }
324
325     if (pkt_version((&reply)) != KRB_PROT_VERSION) {
326         status = ADMIN_BAD_VERSION;
327         goto bad;
328     }
329
330     if ((pkt_msg_type((&reply)) & ~1) != AUTH_MSG_PRIVATE) {
331         bp = reply.dat;
332         if (*bp++ != KRB_PROT_VERSION) {
333             status = ADMIN_BAD_VERSION;
334             goto bad;
335         }
336         if (*bp++ != AUTH_MSG_ERR_REPLY) {
337             status = ADMIN_UNKNOWN_CODE;
338             goto bad;
339         }
340         bp += strlen((char *)bp) + 1;   /* Skip name */
341         bp += strlen((char *)bp) + 1;   /* Skip instance */
342         bp += strlen((char *)bp) + 1;   /* Skip realm */
343
344         /* null-terminate error string */
345         reply.dat[reply.length] = '\0'; 
346
347         if (*bp++ == 126) {
348             status = ADMIN_SERVER_ERROR;
349             strcpy(admin_errmsg, bp);
350             goto bad;
351         } else {
352             status = ADMIN_UNKNOWN_CODE;
353             goto bad;
354         }
355     }
356     status = rd_private_msg(reply.dat, reply.length,
357                             sess_sched, sess_key,
358                             &admin_addr, &my_addr,
359                             &msg_data);
360     if (status) {
361         status += krb_err_base;
362         goto bad;
363     }
364     bp = msg_data.app_data;
365
366     if (*bp++ != PW_SRV_VERSION) {
367         status = ADMIN_BAD_VERSION;
368         goto bad;
369     }
370     if (*bp++ != INSTALL_REPLY) {
371         status = ADMIN_UNKNOWN_CODE;
372         goto bad;
373     }
374
375     status = 0;
376     /* fall through into cleanup */
377
378 bad:
379     /*
380      * Paranoia: shred all the incriminating evidence.
381      * We'll let the caller shred the arguments.
382      */
383     bzero((char *)sess_key, sizeof(sess_key));
384     bzero((char *)sess_sched, sizeof(sess_sched));
385     bzero(pvt_buf, sizeof(pvt_buf));
386     return status;
387 }
388
389 /*
390  * Copies characters from source to dest, returning a pointer to the
391  * point in dest after the last character copied from source.
392  * If this would be greater than end, no characters are copied, and NULL
393  * is returned instead.
394  */
395
396 static u_char *strapp(dest, source, end)
397     register u_char *dest, *source, *end;
398 {
399     register int length = strlen(source) + 1;
400     if (dest+length > end) return NULL;
401     else {
402         bcopy(source, dest, length);
403         return dest + length;
404     }
405 }
406
407 /*
408  * Local Variables:
409  * mode: c
410  * c-indent-level: 4
411  * c-continued-statement-offset: 4
412  * c-brace-offset: -4
413  * c-argdecl-indent: 4
414  * c-label-offset: -4
415  * End:
416  */
This page took 0.274815 seconds and 5 git commands to generate.