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.
RevLine 
[14069]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 */
[7779]15
[14069]16/* This is the server for the lert system. */
[7779]17
[23654]18static const char rcsid[] = "$Id: lertsrv.c,v 1.10 2009/03/26 19:30:39 zacheiss Exp $";
[7779]19
20#include <stdio.h>
[17860]21#include <unistd.h>
[23655]22#ifdef HAVE_KRB4
[7779]23#include <krb.h>
[23655]24#include <des.h>
25#endif
[17860]26#include <krb5.h>
[7779]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>
[22403]33#include <arpa/inet.h>
[7779]34#include <netdb.h>
35#include <sys/time.h>
36#include <hesiod.h>
[8859]37#include <string.h>
[7779]38#include <pwd.h>
39#include <sys/utsname.h>
[11791]40#include <errno.h>
[7779]41#include "lert.h"
42
[22403]43int get_user(char *pname, char *result, int onetime, char *ip);
[17860]44
45krb5_context context = NULL;
46
[14069]47int main(int argc, char **argv)
[7779]48{
49  int s;                        /* The Socket */
50  struct sockaddr_in sin;
51  struct sockaddr_in from;
52  char packet[2048];
[22403]53  char *ip;
[19980]54  char *username = NULL;
[7779]55  int plen;
56  char opacket[2048];
57  int fromlen;
58  int status;
59  int len;
[23655]60#ifdef HAVE_KRB4
[7779]61  AUTH_DAT ad;
62  KTEXT_ST authent;
63  KTEXT_ST ktxt;
[23655]64  char instance[INST_SZ];
[7779]65  /* for krb_mk_priv */
66  des_key_schedule sched;               /* session key schedule */
[23655]67#endif
[7779]68  struct hostent *hp;
69  struct utsname thishost;
[17860]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;
[7779]77
[17860]78  ticket = NULL;
79  memset(&outbuf, 0, sizeof(krb5_data));
80
[7779]81#ifdef LOGGING
[14069]82  setbuf(stdout, NULL);
[7779]83#endif
[14069]84
[7779]85  s = socket(AF_INET, SOCK_DGRAM, 0);
86
[14069]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);
[17860]92    }
[7779]93  hp = gethostbyname(thishost.nodename);
94  if (hp == NULL) {
95    fprintf(stderr, "%s: Unable to get host name information: %s\n",
[11791]96            argv[0], strerror(errno));
[14069]97    exit(1);
[7779]98  }
[17860]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);
[8859]104  memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length);
[17860]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  }
[7779]112
[17860]113  fromlen = sizeof(from);
114
[14069]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        }
[7779]126
127#ifdef DEBUG
[14069]128      fprintf(stderr, "lertsrv: Received packet of length %d\n", plen);
[7779]129#endif
130
[23655]131#ifdef HAVE_KRB4
[17860]132      if (packet[0] == '1')
[14069]133        {
[17860]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
[22403]159            get_user(ad.pname, &(opacket[1]), ad.checksum,
160                     inet_ntoa(from.sin_addr));
[17860]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));
[14069]179        }
[23655]180      else
181#endif
182      if (packet[0] == LERT_VERSION)
[17860]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;
[7779]194
[17860]195          problem =
196            krb5_auth_con_genaddrs(context, auth_con, s,
197                                   KRB5_AUTH_CONTEXT_GENERATE_LOCAL_ADDR);
198          if (problem)
199            goto out;
[7779]200
[17860]201          problem = krb5_sname_to_principal(context, NULL, LERT_SERVICE,
202                                            KRB5_NT_SRV_HST, &server);
203          if (problem)
204            goto out;
[7779]205
[17860]206          problem = krb5_kt_resolve(context, LERTS_KEYTAB, &keytab);
207          if (problem)
208            goto out;
[7779]209
[17860]210          /* Get the authenticator. */
211          auth.data = packet + LERT_LENGTH;
212          auth.length = sizeof(packet) - sizeof(int) - LERT_LENGTH;
[7779]213
[17860]214          problem = krb5_rd_req(context, &auth_con, &auth, server, keytab,
215                                NULL, &ticket);
216          if (problem)
217            goto out;
[7779]218
[17860]219          problem = krb5_copy_principal(context, ticket->enc_part2->client,
220                                        &client);
221          if (problem)
222            goto out;
[7779]223
[17860]224          opacket[0] = LERT_VERSION;
[7779]225
[17860]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           */
[19980]230          if ((krb5_princ_size(context, client) != 1) ||
[17860]231              krb5_realm_compare(context, client, server) == 0)
232            {
233              krb5_unparse_name(context, client, &username);
234              if (username)
[23654]235                fprintf(stderr, "lertsrv: %s not null instance in realm "
236                        "%s.\n", username,
237                        krb5_princ_realm(context, server)->data);
[17860]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           */
[19980]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';
[17860]258
[22403]259              get_user(username, &(opacket[1]), packet[1],
260                       inet_ntoa(from.sin_addr));
[19980]261            }
262
[17860]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);
[19980]292          if (username)
293            free(username);
[17860]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;
[19980]300          username = NULL;
[17860]301        }
302      else
303        {
304          fprintf(stderr, "lertsrv: Received packet with bad version: %d\n",
305                  packet[0]);
306        }
[14069]307    }
[7779]308
[14069]309  return 0;
310}
[7779]311
[22403]312int get_user(char *pname, char *result, int onetime, char *ip)
[7779]313{
314  DBM *db;
315  DBM *db2;
316  datum data;
317  datum key;
318  datum data2;
319  char result2[128];
320
[14069]321  /* Prepare nil return. */
[7779]322  result[0] = LERT_FREE;
323  result[1] = '\0';
324
[14069]325  /* Open the database. */
[7779]326  db = dbm_open(LERTS_DATA, O_RDWR, 0600);
[14069]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    }
[7779]333
334  key.dptr = pname;
335  key.dsize = strlen(pname) + 1;
336
[14069]337  /* Get the user. */
[7779]338  data = dbm_fetch(db, key);
[14069]339  if (data.dptr == NULL)
340    {
341      /* Not in db. */
342      dbm_close(db);
[7779]343#ifdef LOGGING
[22403]344      fprintf(stdout, "lertsrv: %s user %s not in db\n", ip, pname);
[7779]345#endif
[14069]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);
[7779]378        }
[14069]379      dbm_close(db);
[7779]380    }
[14069]381
[7779]382  result[0] = LERT_MSG;
383#ifdef LOGGING
[22403]384  fprintf(stdout, "lertsrv: %s user %s in db with groups :%s:\n", ip, pname,
385          result);
[7779]386#endif
[14069]387  return LERT_GOTCHA;
[7779]388}
389
390
Note: See TracBrowser for help on using the repository browser.