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

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