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