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