source: trunk/athena/bin/lert/lertsrv.c @ 17860

Revision 17860, 9.5 KB checked in by zacheiss, 22 years ago (diff)
Implement v2 of the protocol, which uses krb5 for authentication. Leave v1 protocol support in the server for backwards compatability for now. Remove unused variables and modernize the declaration of main(), too.
Line 
1/* Copyright 1994, 1999 by the Massachusetts Institute of Technology.
2 *
3 * Permission to use, copy, modify, and distribute this
4 * software and its documentation for any purpose and without
5 * fee is hereby granted, provided that the above copyright
6 * notice appear in all copies and that both that copyright
7 * notice and this permission notice appear in supporting
8 * documentation, and that the name of M.I.T. not be used in
9 * advertising or publicity pertaining to distribution of the
10 * software without specific, written prior permission.
11 * M.I.T. makes no representations about the suitability of
12 * this software for any purpose.  It is provided "as is"
13 * without express or implied warranty.
14 */
15
16/* This is the server for the lert system. */
17
18static const char rcsid[] = "$Id: lertsrv.c,v 1.7 2002-09-01 03:31:25 zacheiss Exp $";
19
20#include <stdio.h>
21#include <unistd.h>
22#include <krb.h>
23#include <krb5.h>
24#include <des.h>
25#include <sys/types.h>
26#include <sys/stat.h>
27#include <fcntl.h>
28#include <sys/socket.h>
29#include <sys/uio.h>
30#include <netinet/in.h>
31#include <netdb.h>
32#include <sys/time.h>
33#include <hesiod.h>
34#include <string.h>
35#include <pwd.h>
36#include <sys/utsname.h>
37#include <errno.h>
38#include "lert.h"
39
40int get_user(char *pname, char *result, int onetime);
41
42krb5_context context = NULL;
43
44int main(int argc, char **argv)
45{
46  int s;                        /* The Socket */
47  struct sockaddr_in sin;
48  struct sockaddr_in from;
49  char packet[2048];
50  int plen;
51  char opacket[2048];
52  int fromlen;
53  int status;
54  int len;
55  AUTH_DAT ad;
56  KTEXT_ST authent;
57  KTEXT_ST ktxt;
58  /* for krb_mk_priv */
59  des_key_schedule sched;               /* session key schedule */
60  struct hostent *hp;
61  struct utsname thishost;
62  char instance[INST_SZ];
63  /* for the krb5-authenticated v2 of the protocol */
64  krb5_error_code problem;
65  krb5_auth_context auth_con = NULL;
66  krb5_data auth, inbuf, outbuf;
67  krb5_principal server = NULL, client = NULL;
68  krb5_ticket *ticket;
69  krb5_keytab keytab = NULL;
70
71  ticket = NULL;
72  memset(&outbuf, 0, sizeof(krb5_data));
73
74#ifdef LOGGING
75  setbuf(stdout, NULL);
76#endif
77
78  s = socket(AF_INET, SOCK_DGRAM, 0);
79
80  if (uname(&thishost) == -1)
81    {
82      fprintf(stderr, "%s: Unable to get system information: %s\n",
83              argv[0], strerror(errno));
84      exit(1);
85    }
86  hp = gethostbyname(thishost.nodename);
87  if (hp == NULL) {
88    fprintf(stderr, "%s: Unable to get host name information: %s\n",
89            argv[0], strerror(errno));
90    exit(1);
91  }
92 
93  /* Clear the socket structure. */
94  memset(&sin, 0, sizeof(sin));
95  sin.sin_family = AF_INET;
96  sin.sin_port = htons(LERT_PORT);
97  memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length);
98 
99  if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 0)
100    {
101      fprintf(stderr, "%s: Unable to bind socket: %s\n",
102              argv[0], strerror(errno));
103      exit(1);
104  }
105
106  fromlen = sizeof(from);
107
108  for (;;)
109    {
110      plen = recvfrom(s, packet, sizeof(packet), 0,
111                      (struct sockaddr *)&from, &fromlen);
112      if (plen < 0)
113        {
114          fprintf(stderr, "lertsrv: Error during recv: %s\n",
115                  strerror(errno));
116          sleep(1);             /* Prevent infinite cpu hog loop */
117          continue;
118        }
119
120#ifdef DEBUG
121      fprintf(stderr, "lertsrv: Received packet of length %d\n", plen);
122#endif
123
124      if (packet[0] == '1')
125        {
126          /* krb4-authenticated version of the protocol. */
127
128          /* Copy authenticator into aligned region. */
129          memcpy(&authent, &packet[LERT_LENGTH], sizeof(authent));
130         
131          strcpy(instance, "*");
132          status = krb_rd_req(&authent, LERT_SERVICE, instance, 0, &ad,
133                              LERTS_SRVTAB);
134         
135          if (status != KSUCCESS)
136            {
137              fprintf(stderr, "lertsrv: Kerberos failure: %s\n",
138                      krb_err_txt[status]);
139              continue;
140            }
141         
142          opacket[0] = '1';
143          if ((strlen(ad.pinst) != 0) || (strcmp(ad.prealm, "ATHENA.MIT.EDU")))
144            {
145              fprintf(stderr, "lertsrv: %s.%s@%s -- Not null instance ATHENA "
146                      "realm.\n", ad.pname, ad.pinst, ad.prealm);
147              opacket[1] = LERT_NOT_IN_DB;
148              opacket[2] = '\0';
149            }
150          else
151            get_user(ad.pname, &(opacket[1]), ad.checksum);
152         
153          /* Prepare krb_mk_priv message */
154          des_key_sched(ad.session, sched);
155         
156          /* Make the encrypted message:
157           * version, code, data, plus terminating '\0'
158           */
159          len = krb_mk_priv(opacket, ktxt.dat,
160                            LERT_CHECK + strlen(opacket + LERT_CHECK) + 1,
161                            sched, ad.session, &sin, &from);
162         
163          /* Send it. */
164          if (sendto(s, ktxt.dat, len, 0,
165                     (struct sockaddr *) &from, sizeof(from)) < 0)
166            fprintf(stderr, "lertsrv: sendto failed: %s\n", strerror(errno));
167         
168          /* Avoid confusion, zeroize now */
169          memset(&from, 0, sizeof(from));
170        }
171      else if (packet[0] == LERT_VERSION)
172        {
173          /* krb5-authenticated version of the protocol. */
174          if (!context)
175            {
176              problem = krb5_init_context(&context);
177              if (problem)
178                goto out;
179            }
180          problem = krb5_auth_con_init(context, &auth_con);
181          if (problem)
182            goto out;
183
184          problem =
185            krb5_auth_con_genaddrs(context, auth_con, s,
186                                   KRB5_AUTH_CONTEXT_GENERATE_LOCAL_ADDR);
187          if (problem)
188            goto out;
189
190          problem = krb5_sname_to_principal(context, NULL, LERT_SERVICE,
191                                            KRB5_NT_SRV_HST, &server);
192          if (problem)
193            goto out;
194
195          problem = krb5_kt_resolve(context, LERTS_KEYTAB, &keytab);
196          if (problem)
197            goto out;
198
199          /* Get the authenticator. */
200          auth.data = packet + LERT_LENGTH;
201          auth.length = sizeof(packet) - sizeof(int) - LERT_LENGTH;
202
203          problem = krb5_rd_req(context, &auth_con, &auth, server, keytab,
204                                NULL, &ticket);
205          if (problem)
206            goto out;
207
208          problem = krb5_copy_principal(context, ticket->enc_part2->client,
209                                        &client);
210          if (problem)
211            goto out;
212
213          opacket[0] = LERT_VERSION;
214
215          /* If the client is something other than null instance principal
216           * or is from a realm other than the server's local realm, punt
217           * them.
218           */
219          if ((krb5_princ_component(context, client, 1)->data) ||
220              krb5_realm_compare(context, client, server) == 0)
221            {
222              char *username = NULL;
223              krb5_unparse_name(context, client, &username);
224              if (username)
225                {
226                  fprintf(stderr, "lertsrv: %s not null instance in realm "
227                          "%s.\n", username,
228                          krb5_princ_realm(context, server)->data);
229                  free(username);
230                }
231              opacket[1] = LERT_NOT_IN_DB;
232              opacket[2] = '\0';
233            }
234          else
235          /* packet[1] will be a 0 or 1 depending on whether or not the
236           * user called lert with the -n flag to indicate they don't
237           * want to see the message anymore.
238           *
239           * The krb4 code passes in the checksum member of the AUTH_DAT
240           * structure filled in by krb_rd_req().  While it will happen to
241           * be 0 if the user didn't call lert with the -n flag, using
242           * the value from the packet directly seems a bit more sane.
243           */
244            get_user((krb5_princ_component(context, client, 0)->data),
245                     &(opacket[1]), packet[1]);
246
247          inbuf.data = opacket;
248          inbuf.length = strlen(opacket);
249
250          problem = krb5_mk_priv(context, auth_con, &inbuf, &outbuf, NULL);
251          if (problem)
252            goto out;
253
254          if (sendto(s, outbuf.data, outbuf.length, 0,
255                     (struct sockaddr *)&from, sizeof(from)) < 0)
256            fprintf(stderr, "lertsrv: sendto failed: %s\n", strerror(errno));
257
258          /* Avoid confusion, zeroize now */
259          memset(&from, 0, sizeof(from));
260
261    out:
262          if (problem)
263            fprintf(stderr, "lertsrv: Kerberos error: %s\n",
264                    error_message(problem));
265          /* krb5 library checks if this is allocated for us. */
266          krb5_free_data_contents(context, &outbuf);
267          memset(&outbuf, 0, sizeof(krb5_data));
268          if (client)
269            krb5_free_principal(context, client);
270          if (server)
271            krb5_free_principal(context, server);
272          if (ticket)
273            krb5_free_ticket(context, ticket);
274          if (auth_con)
275            krb5_auth_con_free(context, auth_con);
276          /* reset to NULL after freeing since we're going to go through
277           * this loop again.
278           */
279          client = server = NULL;
280          ticket = NULL;
281          auth_con = NULL;
282        }
283      else
284        {
285          fprintf(stderr, "lertsrv: Received packet with bad version: %d\n",
286                  packet[0]);
287        }
288    }
289
290  return 0;
291}
292
293int get_user(char *pname, char *result, int onetime)
294{
295  DBM *db;
296  DBM *db2;
297  datum data;
298  datum key;
299  datum data2;
300  char result2[128];
301
302  /* Prepare nil return. */
303  result[0] = LERT_FREE;
304  result[1] = '\0';
305
306  /* Open the database. */
307  db = dbm_open(LERTS_DATA, O_RDWR, 0600);
308  if (db == NULL)
309    {
310      fprintf(stderr, "Unable to open lert's database file %s: %s.\n",
311              LERTS_DATA, strerror(errno));
312      return LERT_NO_DB;
313    }
314
315  key.dptr = pname;
316  key.dsize = strlen(pname) + 1;
317
318  /* Get the user. */
319  data = dbm_fetch(db, key);
320  if (data.dptr == NULL)
321    {
322      /* Not in db. */
323      dbm_close(db);
324#ifdef LOGGING
325      fprintf(stdout, "lertsrv: user %s not in db\n", pname);
326#endif
327      return LERT_NOT_IN_DB;
328    }
329  else
330    {
331      strncpy(&(result[1]), data.dptr, data.dsize);
332      result[data.dsize + 1] = '\0';
333
334      if (onetime)
335        {
336          /* Add them to the log. */
337          db2 = dbm_open(LERTS_LOG, O_RDWR | O_CREAT, 0600);
338          if (db2 == NULL)
339            {
340              fprintf(stderr, "Unable to open lert's log database %s: %s.\n",
341                      LERTS_LOG, strerror(errno));
342            }
343          else
344            {
345              data2 = dbm_fetch(db2, key);
346              if (data2.dptr == NULL)
347                dbm_store(db2, key, data, DBM_INSERT);
348              else
349                {
350                  strncpy(result2, data2.dptr, data2.dsize);
351                  strncpy(&(result2[data2.dsize]), data.dptr, data.dsize);
352                  data2.dptr = result2;
353                  data2.dsize += data.dsize;
354                  dbm_store(db2, key, data2, DBM_REPLACE);
355                }
356              dbm_close(db2);
357            }
358          dbm_delete(db, key);
359        }
360      dbm_close(db);
361    }
362
363  result[0] = LERT_MSG;
364#ifdef LOGGING
365  fprintf(stdout, "lertsrv: user %s in db with groups :%s:\n", pname, result);
366#endif
367  return LERT_GOTCHA;
368}
369
370
Note: See TracBrowser for help on using the repository browser.