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

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