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

Revision 23654, 9.9 KB checked in by broder, 15 years ago (diff)
In lert: * Fix a double-free in the lert server. (Patch from CVS by Garry Zacheiss)
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#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                fprintf(stderr, "lertsrv: %s not null instance in realm "
229                        "%s.\n", username,
230                        krb5_princ_realm(context, server)->data);
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            {
245              username = malloc(krb5_princ_component(context, client, 0)->length + 1);
246              if (!username)
247                goto out;
248              strncpy(username, krb5_princ_component(context, client, 0)->data,
249                      krb5_princ_component(context, client, 0)->length);
250              username[krb5_princ_component(context, client, 0)->length] = '\0';
251
252              get_user(username, &(opacket[1]), packet[1],
253                       inet_ntoa(from.sin_addr));
254            }
255
256          inbuf.data = opacket;
257          inbuf.length = strlen(opacket);
258
259          problem = krb5_mk_priv(context, auth_con, &inbuf, &outbuf, NULL);
260          if (problem)
261            goto out;
262
263          if (sendto(s, outbuf.data, outbuf.length, 0,
264                     (struct sockaddr *)&from, sizeof(from)) < 0)
265            fprintf(stderr, "lertsrv: sendto failed: %s\n", strerror(errno));
266
267          /* Avoid confusion, zeroize now */
268          memset(&from, 0, sizeof(from));
269
270    out:
271          if (problem)
272            fprintf(stderr, "lertsrv: Kerberos error: %s\n",
273                    error_message(problem));
274          /* krb5 library checks if this is allocated for us. */
275          krb5_free_data_contents(context, &outbuf);
276          memset(&outbuf, 0, sizeof(krb5_data));
277          if (client)
278            krb5_free_principal(context, client);
279          if (server)
280            krb5_free_principal(context, server);
281          if (ticket)
282            krb5_free_ticket(context, ticket);
283          if (auth_con)
284            krb5_auth_con_free(context, auth_con);
285          if (username)
286            free(username);
287          /* reset to NULL after freeing since we're going to go through
288           * this loop again.
289           */
290          client = server = NULL;
291          ticket = NULL;
292          auth_con = NULL;
293          username = NULL;
294        }
295      else
296        {
297          fprintf(stderr, "lertsrv: Received packet with bad version: %d\n",
298                  packet[0]);
299        }
300    }
301
302  return 0;
303}
304
305int get_user(char *pname, char *result, int onetime, char *ip)
306{
307  DBM *db;
308  DBM *db2;
309  datum data;
310  datum key;
311  datum data2;
312  char result2[128];
313
314  /* Prepare nil return. */
315  result[0] = LERT_FREE;
316  result[1] = '\0';
317
318  /* Open the database. */
319  db = dbm_open(LERTS_DATA, O_RDWR, 0600);
320  if (db == NULL)
321    {
322      fprintf(stderr, "Unable to open lert's database file %s: %s.\n",
323              LERTS_DATA, strerror(errno));
324      return LERT_NO_DB;
325    }
326
327  key.dptr = pname;
328  key.dsize = strlen(pname) + 1;
329
330  /* Get the user. */
331  data = dbm_fetch(db, key);
332  if (data.dptr == NULL)
333    {
334      /* Not in db. */
335      dbm_close(db);
336#ifdef LOGGING
337      fprintf(stdout, "lertsrv: %s user %s not in db\n", ip, pname);
338#endif
339      return LERT_NOT_IN_DB;
340    }
341  else
342    {
343      strncpy(&(result[1]), data.dptr, data.dsize);
344      result[data.dsize + 1] = '\0';
345
346      if (onetime)
347        {
348          /* Add them to the log. */
349          db2 = dbm_open(LERTS_LOG, O_RDWR | O_CREAT, 0600);
350          if (db2 == NULL)
351            {
352              fprintf(stderr, "Unable to open lert's log database %s: %s.\n",
353                      LERTS_LOG, strerror(errno));
354            }
355          else
356            {
357              data2 = dbm_fetch(db2, key);
358              if (data2.dptr == NULL)
359                dbm_store(db2, key, data, DBM_INSERT);
360              else
361                {
362                  strncpy(result2, data2.dptr, data2.dsize);
363                  strncpy(&(result2[data2.dsize]), data.dptr, data.dsize);
364                  data2.dptr = result2;
365                  data2.dsize += data.dsize;
366                  dbm_store(db2, key, data2, DBM_REPLACE);
367                }
368              dbm_close(db2);
369            }
370          dbm_delete(db, key);
371        }
372      dbm_close(db);
373    }
374
375  result[0] = LERT_MSG;
376#ifdef LOGGING
377  fprintf(stdout, "lertsrv: %s user %s in db with groups :%s:\n", ip, pname,
378          result);
379#endif
380  return LERT_GOTCHA;
381}
382
383
Note: See TracBrowser for help on using the repository browser.