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