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