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