source: trunk/athena/bin/lert/lert.c @ 23692

Revision 23692, 11.2 KB checked in by broder, 16 years ago (diff)
In lert: * Try $ATHENA_USER before using $USER.
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 client for the lert system. */
17
18static const char rcsid[] = "$Id: lert.c,v 1.12 2002-09-01 03:31:25 zacheiss Exp $";
19
20#include <sys/types.h>
21#include <sys/socket.h>
22#include <sys/stat.h>
23#include <sys/time.h>
24
25#include <errno.h>
26#include <fcntl.h>
27#include <netdb.h>
28#include <netinet/in.h>
29#include <pwd.h>
30#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
33#include <unistd.h>
34
35#include <hesiod.h>
36#include <krb5.h>
37
38#include "lert.h"
39
40#define argis(a, b) (!strcmp(*arg + 1, a) || !strcmp(*arg + 1, b))
41
42static struct timeval timeout = { LERT_TIMEOUT, 0 };
43static int error_messages = TRUE;
44
45static void bombout(int mess);
46
47static void usage(char *pname)
48{
49  fprintf(stderr,
50          "Usage: %s [-zephyr|-z] [-mail|-m] [-no|-n] [-quiet|-q] [-server|-s server]\n", pname);
51}
52
53static char *lert_says(int no_more, char *server)
54{
55  krb5_context context = NULL;
56  krb5_auth_context auth_con = NULL;
57  krb5_ccache ccache = NULL;
58  krb5_data auth, packet, msg_data;
59  krb5_error_code problem = 0;
60  void *hes_context = NULL;
61  char *lert_host, *message;
62  struct hostent *hp;
63  struct servent *sp;
64  struct sockaddr_in sin;
65  int plen, packetsize, s, tries, gotit, nread;
66  unsigned char *pktbuf;
67  fd_set readfds;
68
69  memset(&auth, 0, sizeof(krb5_data));
70  memset(&packet, 0, sizeof(krb5_data));
71  memset(&msg_data, 0, sizeof(krb5_data));
72
73  /* Find out where lert lives. (Note the presumption that there is
74   * only one lert!)
75   */
76  lert_host = NULL;
77  if (server)
78    lert_host = server;
79  else if (hesiod_init(&hes_context) == 0)
80    {
81      char **slocs = hesiod_resolve(hes_context, LERT_SERVER, LERT_TYPE);
82      if (slocs)
83        {
84          lert_host = strdup(slocs[0]);
85          if (!lert_host)
86            bombout(ERR_MEMORY);
87          hesiod_free_list(hes_context, slocs);
88        }
89    }
90  if (!lert_host)
91    {
92      lert_host = strdup(LERT_HOME);
93      if (!lert_host)
94        bombout(ERR_MEMORY);
95    }
96
97  hp = gethostbyname(lert_host);
98  if (hp == NULL)
99    bombout(ERR_HOSTNAME);
100
101  memset(&sin, 0, sizeof(sin));
102  sin.sin_family = hp->h_addrtype;
103  memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr));
104
105  /* Find out what port lert is on. */
106  sin.sin_port = htons(LERT_PORT);
107  sp = getservbyname(LERT_SERVED, LERT_PROTO);
108  if (sp)
109    sin.sin_port = sp->s_port;
110  else if (hes_context)
111    {
112      sp = hesiod_getservbyname(hes_context, LERT_SERVED, LERT_PROTO);
113      sin.sin_port = sp->s_port;
114      hesiod_free_servent(hes_context, sp);
115    }
116   
117  if (hes_context)
118    hesiod_end(hes_context);
119
120  problem = krb5_init_context(&context);
121  if (problem)
122    goto out;
123
124  problem = krb5_auth_con_init(context, &auth_con);
125  if (problem)
126    goto out;
127
128  problem = krb5_cc_default(context, &ccache);
129  if (problem)
130    goto out;
131
132  problem = krb5_mk_req(context, &auth_con, 0, LERT_SERVICE, lert_host,
133                        NULL, ccache, &auth);
134  if (problem)
135    goto out;
136
137  /* Lert's basic protocol:
138   * Client sends version, one byte query code, and authentication.
139   */
140  plen = LERT_LENGTH + sizeof(int) + auth.length;
141  pktbuf = malloc(plen);
142  if (!pktbuf)
143    bombout(ERR_MEMORY);
144  pktbuf[0] = LERT_VERSION;
145  pktbuf[1] = no_more;
146  memcpy(pktbuf + LERT_LENGTH, auth.data, auth.length);
147
148  s = socket(AF_INET, SOCK_DGRAM, 0);
149  if (s < 0)
150    bombout(ERR_SOCKET);
151  if (connect(s, (struct sockaddr *)&sin, sizeof(struct sockaddr_in)) < 0)
152    bombout(ERR_CONNECT);
153
154  gotit = 0;
155  for (tries = 0; tries < RETRIES && !gotit; tries++)
156    {
157      if (send(s, pktbuf, plen, 0) < 0)
158        bombout(ERR_SEND);
159      FD_ZERO(&readfds);
160      FD_SET(s, &readfds);
161      gotit = select(s + 1, &readfds, NULL, NULL, &timeout) == 1;
162    }
163  free(pktbuf);
164  if (tries == RETRIES)
165    bombout(ERR_TIMEOUT);
166
167  /* Read the response. */
168  packetsize = 2048;
169  pktbuf = malloc(packetsize);
170  if (!pktbuf)
171    bombout(ERR_MEMORY);
172  plen = 0;
173  while (1)
174    {
175      nread = recv(s, pktbuf + plen, packetsize - plen, 0);
176      if (nread < 0)
177        bombout(ERR_RCV);
178      plen += nread;
179
180      if (nread < packetsize - plen)
181        break;
182
183      packetsize *= 2;
184      pktbuf = realloc(pktbuf, packetsize);
185      if (!pktbuf)
186        bombout(ERR_MEMORY);
187    }
188
189  packet.data = pktbuf;
190  packet.length = nread;
191
192  problem = krb5_auth_con_genaddrs(context, auth_con, s,
193                                   KRB5_AUTH_CONTEXT_GENERATE_REMOTE_ADDR |
194                                   KRB5_AUTH_CONTEXT_GENERATE_LOCAL_ADDR);
195  if (problem)
196    goto out;
197
198  /* Now close the socket. */
199  shutdown(s, 2);
200  close(s);
201
202  /* Clear auth_context flags so we don't need to manually set up a
203   * replay cache.
204   */
205  problem = krb5_auth_con_setflags(context, auth_con, 0);
206  if (problem)
207    goto out;
208
209  problem = krb5_rd_priv(context, auth_con, &packet, &msg_data, NULL);
210  if (problem)
211    goto out;
212
213  if (msg_data.length == 0)
214    return NULL;
215
216  /* At this point, we have a packet.  Check it out:
217   * [0] LERT_VERSION
218   * [1] code response
219   * [2 on] data...
220   */
221
222  if (msg_data.data[0] != LERT_VERSION)
223    bombout(ERR_VERSION);
224
225  if (msg_data.data[1] == LERT_MSG)
226    {
227      /* We have a message from the server. */
228      message = malloc(msg_data.length - LERT_CHECK + 1);
229      if (!message)
230        bombout(ERR_MEMORY);
231      memcpy(message, msg_data.data + LERT_CHECK,
232           msg_data.length - LERT_CHECK);
233      message[msg_data.length - LERT_CHECK] = '\0';
234    }
235  else
236    message = NULL;
237  free(pktbuf);
238
239  return message;
240
241 out:
242  /* krb5 library checks if this is allocated so we don't have to. */
243  krb5_free_data_contents(context, &auth);
244  krb5_free_data_contents(context, &msg_data);
245
246  if (ccache)
247    krb5_cc_close(context, ccache);
248
249  if (auth_con)
250    krb5_auth_con_free(context, auth_con);
251
252  bombout(problem);
253}
254
255/* General error reporting */
256static void bombout(int mess)
257{
258  if (error_messages)
259    {
260      fprintf(stderr, "lert: ");
261      switch(mess)
262        {
263        case ERR_KERB_CRED:
264          fprintf(stderr, "Error getting kerberos credentials.\n");
265          break;
266        case ERR_KERB_AUTH:
267          fprintf(stderr, "Error getting kerberos authentication.\n");
268          fprintf(stderr, "Are your tickets valid?\n");
269          break;
270        case ERR_TIMEOUT:
271          fprintf(stderr, "Timed out waiting for response from server.\n");
272          break;
273        case ERR_SERVER:
274          fprintf(stderr, "Unable to contact server.\n");
275          break;
276        case ERR_SERVED:
277          fprintf(stderr, "Bad string from server.\n");
278          break;
279        case ERR_SEND:
280          fprintf(stderr, "Error in send: %s\n", strerror(errno));
281          break;
282        case ERR_RCV:
283          fprintf(stderr, "Error in recv: %s\n", strerror(errno));
284          break;
285        case ERR_USER:
286          fprintf(stderr, "Could not get your name to send messages\n");
287          break;
288        case NO_PROCS:
289          fprintf(stderr, "Error running child processes: %s\n",
290                  strerror(errno));
291          break;
292        case ERR_MEMORY:
293          fprintf(stderr, "Out of memory\n");
294          break;
295        case ERR_FILE:
296          fprintf(stderr, "Could not read message file\n");
297          break;
298        default:
299          fprintf(stderr, "%s while accessing the database\n",
300                  error_message(mess));
301          break;
302        }
303      fprintf(stderr, "Please try again later.\n");
304      fprintf(stderr,
305              "If this problem persists, please report it to a consultant.\n");
306    }
307  exit(1);
308}
309
310/* Send a message from lert via zephyr. */
311static void zephyr_message(char *user, char *header, char *message)
312{
313  FILE *p;
314  char *cmd;
315
316  cmd = malloc(11 + strlen(user));
317  if (!cmd)
318    bombout(ERR_MEMORY);
319  sprintf(cmd, "zwrite -q %s", user);
320
321  p = popen(cmd, "w");
322  if (!p)
323    bombout(NO_PROCS);
324  fputs("@bold{", p);
325  fputs(header, p);
326  fputs("}", p);
327  fputs(message, p);
328  pclose(p);
329}
330
331/* Send a message from lert via mail. */
332static void mail_message(char *user, char *subject,
333                         char *header, char *message)
334{
335  FILE *p;
336  char *cmd;
337
338  cmd = malloc(20 + strlen(subject) + strlen(user));
339  if (!cmd)
340    bombout(ERR_MEMORY);
341  sprintf(cmd, "mhmail %s -subject \"%s\"", user, subject);
342
343  p = popen(cmd, "w");
344  if (!p)
345    bombout(NO_PROCS);
346  fputs(header, p);
347  fputs(message, p);
348  pclose(p);
349}
350
351/* Print a message from lert to stdout. */
352static void cat_message(char *header, char *message)
353{
354  fputs(header, stdout);
355  fputs(message, stdout);
356}         
357
358/* Read a file from the lert directory. */
359static char *read_file(char *name)
360{
361  struct stat st;
362  int fd;
363  char *buf;
364
365  fd = open(name, O_RDONLY);
366  if (fd < 0 || fstat(fd, &st) < 0)
367    bombout(ERR_FILE);
368
369  buf = malloc(st.st_size + 1);
370  if (!buf)
371    bombout(ERR_MEMORY);
372
373  if (read(fd, buf, st.st_size) < st.st_size)
374    bombout(ERR_FILE);
375  buf[st.st_size] = '\0';
376  close(fd);
377
378  return buf;
379}
380
381/* View the message from lert. */
382static void view_message(char *msgs, int type, int no_more)
383{
384  char name[sizeof(LERTS_MSG_FILES) + 2];
385  char *user, *subject = NULL;
386  char *header, *body = NULL;
387
388  if (type == LERT_Z || type == LERT_MAIL)
389    {
390      user = getenv("ATHENA_USER");
391      if(!user)
392        user = getenv("USER");
393
394      if(!user)
395        user = getlogin();
396
397      if(!user)
398        {
399          struct passwd *pw;
400          pw = getpwuid(getuid());
401          if (pw)
402            user = pw->pw_name;
403        }
404
405      if (!user)
406        type = LERT_CAT;
407    }
408
409  if (type == LERT_MAIL)
410    {
411      subject = read_file(LERTS_MSG_SUBJECT);
412      if (!subject)
413        subject = strdup(LERTS_DEF_SUBJECT);
414      if (!subject)
415        bombout(ERR_MEMORY);
416    }
417
418  header = read_file(no_more ? LERTS_MSG_FILES "1" : LERTS_MSG_FILES "0");
419
420  while (*msgs)
421    {
422      sprintf(name, "%s%c", LERTS_MSG_FILES, *msgs);
423      body = read_file(name);
424      switch (type)
425        {
426        case LERT_Z:
427          zephyr_message(user, header, body);
428          break;
429
430        case LERT_MAIL:
431          mail_message(user, subject, header, body);
432          break;
433
434        default:
435          cat_message(header, body);
436          break;
437        }
438
439      free(body);
440      msgs++;
441    }
442
443  free(header);
444  free(subject);
445}
446
447int main(int argc, char **argv)
448{
449  char *whoami, *message, *server = NULL;
450  int method = LERT_CAT, no_more = FALSE;
451  char **arg = argv;
452
453  whoami = strrchr(argv[0], '/');
454  if (whoami)
455    whoami++;
456  else
457    whoami = argv[0];
458
459  /* Argument Processing:
460   *    -z or -zephyr: send the message as a zephyrgram.
461   *    -m or -mail: send the message as email
462   *    -n or -no: no more messages!
463   */
464
465  while (++arg - argv < argc)
466    {
467      if (**arg == '-')
468        {
469          if (argis("zephyr", "z"))
470            method = LERT_Z;
471          else if (argis("mail","m"))
472            method = LERT_MAIL;
473          else if (argis("no", "n"))
474            no_more = TRUE;
475          else if (argis("quiet", "q"))
476            error_messages = FALSE;
477          else if (argis("server", "s"))
478            {
479              if (arg - argv < argc - 1)
480                {
481                  ++arg;
482                  server = *arg;
483                }
484              else
485                usage(whoami);
486            }
487          else
488            {
489              usage(whoami);
490              exit(1);
491            }
492        }
493    }
494  /* Get the server's string for this user. */
495  message = lert_says(no_more, server);
496  if (message)
497    {
498      view_message(message, method, no_more);
499      free(message);
500    }
501
502  return 0;
503}
Note: See TracBrowser for help on using the repository browser.