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.
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 */
[7780]15
[14069]16/* This is the client for the lert system. */
[7780]17
[17860]18static const char rcsid[] = "$Id: lert.c,v 1.12 2002-09-01 03:31:25 zacheiss Exp $";
[7780]19
20#include <sys/types.h>
[14073]21#include <sys/socket.h>
[7780]22#include <sys/stat.h>
[14073]23#include <sys/time.h>
24
25#include <errno.h>
[7780]26#include <fcntl.h>
27#include <netdb.h>
[14073]28#include <netinet/in.h>
[7780]29#include <pwd.h>
[14073]30#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
33#include <unistd.h>
34
35#include <hesiod.h>
[17860]36#include <krb5.h>
[14073]37
[7780]38#include "lert.h"
39
[14857]40#define argis(a, b) (!strcmp(*arg + 1, a) || !strcmp(*arg + 1, b))
41
[7780]42static struct timeval timeout = { LERT_TIMEOUT, 0 };
43static int error_messages = TRUE;
44
[14069]45static void bombout(int mess);
46
[14857]47static void usage(char *pname)
[7780]48{
[10066]49  fprintf(stderr,
[14857]50          "Usage: %s [-zephyr|-z] [-mail|-m] [-no|-n] [-quiet|-q] [-server|-s server]\n", pname);
[7780]51}
52
[14857]53static char *lert_says(int no_more, char *server)
[7780]54{
[17860]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;
[14073]60  void *hes_context = NULL;
61  char *lert_host, *message;
[7780]62  struct hostent *hp;
[14073]63  struct servent *sp;
[17860]64  struct sockaddr_in sin;
65  int plen, packetsize, s, tries, gotit, nread;
66  unsigned char *pktbuf;
[14073]67  fd_set readfds;
[7780]68
[17860]69  memset(&auth, 0, sizeof(krb5_data));
70  memset(&packet, 0, sizeof(krb5_data));
71  memset(&msg_data, 0, sizeof(krb5_data));
72
[14069]73  /* Find out where lert lives. (Note the presumption that there is
74   * only one lert!)
[7780]75   */
[14073]76  lert_host = NULL;
[14857]77  if (server)
78    lert_host = server;
79  else if (hesiod_init(&hes_context) == 0)
[14069]80    {
[14073]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        }
[14069]89    }
[14073]90  if (!lert_host)
91    {
92      lert_host = strdup(LERT_HOME);
93      if (!lert_host)
94        bombout(ERR_MEMORY);
95    }
[7780]96
[14073]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
[17860]120  problem = krb5_init_context(&context);
121  if (problem)
122    goto out;
[7780]123
[17860]124  problem = krb5_auth_con_init(context, &auth_con);
125  if (problem)
126    goto out;
[7780]127
[17860]128  problem = krb5_cc_default(context, &ccache);
129  if (problem)
130    goto out;
[7780]131
[17860]132  problem = krb5_mk_req(context, &auth_con, 0, LERT_SERVICE, lert_host,
133                        NULL, ccache, &auth);
134  if (problem)
135    goto out;
[7780]136
[14069]137  /* Lert's basic protocol:
[14073]138   * Client sends version, one byte query code, and authentication.
[14069]139   */
[17860]140  plen = LERT_LENGTH + sizeof(int) + auth.length;
141  pktbuf = malloc(plen);
142  if (!pktbuf)
[14073]143    bombout(ERR_MEMORY);
[17860]144  pktbuf[0] = LERT_VERSION;
145  pktbuf[1] = no_more;
146  memcpy(pktbuf + LERT_LENGTH, auth.data, auth.length);
[7780]147
148  s = socket(AF_INET, SOCK_DGRAM, 0);
[14069]149  if (s < 0)
150    bombout(ERR_SOCKET);
151  if (connect(s, (struct sockaddr *)&sin, sizeof(struct sockaddr_in)) < 0)
152    bombout(ERR_CONNECT);
[14073]153
[7780]154  gotit = 0;
[14073]155  for (tries = 0; tries < RETRIES && !gotit; tries++)
[14069]156    {
[17860]157      if (send(s, pktbuf, plen, 0) < 0)
[14069]158        bombout(ERR_SEND);
159      FD_ZERO(&readfds);
160      FD_SET(s, &readfds);
[14073]161      gotit = select(s + 1, &readfds, NULL, NULL, &timeout) == 1;
162    }
[17860]163  free(pktbuf);
[15812]164  if (tries == RETRIES)
[14073]165    bombout(ERR_TIMEOUT);
[7780]166
[14073]167  /* Read the response. */
168  packetsize = 2048;
[17860]169  pktbuf = malloc(packetsize);
170  if (!pktbuf)
[14073]171    bombout(ERR_MEMORY);
172  plen = 0;
173  while (1)
174    {
[17860]175      nread = recv(s, pktbuf + plen, packetsize - plen, 0);
[14073]176      if (nread < 0)
[14069]177        bombout(ERR_RCV);
[14073]178      plen += nread;
[7780]179
[14073]180      if (nread < packetsize - plen)
181        break;
182
183      packetsize *= 2;
[17860]184      pktbuf = realloc(pktbuf, packetsize);
185      if (!pktbuf)
[14073]186        bombout(ERR_MEMORY);
[14069]187    }
188
[17860]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
[14073]198  /* Now close the socket. */
[14069]199  shutdown(s, 2);
[7780]200  close(s);
201
[17860]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;
[7780]208
[17860]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)
[14073]214    return NULL;
[7780]215
[14069]216  /* At this point, we have a packet.  Check it out:
217   * [0] LERT_VERSION
218   * [1] code response
219   * [2 on] data...
220   */
[7780]221
[17860]222  if (msg_data.data[0] != LERT_VERSION)
[14069]223    bombout(ERR_VERSION);
[14073]224
[17860]225  if (msg_data.data[1] == LERT_MSG)
[14069]226    {
227      /* We have a message from the server. */
[17860]228      message = malloc(msg_data.length - LERT_CHECK + 1);
[14073]229      if (!message)
230        bombout(ERR_MEMORY);
[17860]231      memcpy(message, msg_data.data + LERT_CHECK,
232           msg_data.length - LERT_CHECK);
233      message[msg_data.length - LERT_CHECK] = '\0';
[14069]234    }
235  else
[14073]236    message = NULL;
[17860]237  free(pktbuf);
[14073]238
239  return message;
[17860]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);
[7780]253}
254
[14069]255/* General error reporting */
256static void bombout(int mess)
[7780]257{
[14069]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;
[14073]295        case ERR_FILE:
296          fprintf(stderr, "Could not read message file\n");
297          break;
[14069]298        default:
[17860]299          fprintf(stderr, "%s while accessing the database\n",
300                  error_message(mess));
[14069]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");
[7780]306    }
[14069]307  exit(1);
[7780]308}
309
[14073]310/* Send a message from lert via zephyr. */
311static void zephyr_message(char *user, char *header, char *message)
[7780]312{
[14073]313  FILE *p;
314  char *cmd;
[7780]315
[14073]316  cmd = malloc(11 + strlen(user));
317  if (!cmd)
318    bombout(ERR_MEMORY);
319  sprintf(cmd, "zwrite -q %s", user);
[7780]320
[14073]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}
[14069]330
[14073]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;
[14069]337
[14073]338  cmd = malloc(20 + strlen(subject) + strlen(user));
339  if (!cmd)
[14069]340    bombout(ERR_MEMORY);
[14073]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);
[7780]349}
350
[14073]351/* Print a message from lert to stdout. */
352static void cat_message(char *header, char *message)
[7780]353{
[14073]354  fputs(header, stdout);
355  fputs(message, stdout);
356}         
[7780]357
[14073]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;
[7780]364
[14073]365  fd = open(name, O_RDONLY);
366  if (fd < 0 || fstat(fd, &st) < 0)
367    bombout(ERR_FILE);
[7780]368
[14073]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. */
[14081]382static void view_message(char *msgs, int type, int no_more)
[14073]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)
[14069]389    {
[23692]390      user = getenv("ATHENA_USER");
391      if(!user)
392        user = getenv("USER");
[14073]393
[23692]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;
[14073]403        }
[23692]404
[14073]405      if (!user)
406        type = LERT_CAT;
[7780]407    }
[14069]408
[14073]409  if (type == LERT_MAIL)
[14069]410    {
[14073]411      subject = read_file(LERTS_MSG_SUBJECT);
412      if (!subject)
413        subject = strdup(LERTS_DEF_SUBJECT);
414      if (!subject)
415        bombout(ERR_MEMORY);
416    }
[7780]417
[14081]418  header = read_file(no_more ? LERTS_MSG_FILES "1" : LERTS_MSG_FILES "0");
[7780]419
[14073]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;
[7780]429
[14073]430        case LERT_MAIL:
431          mail_message(user, subject, header, body);
432          break;
[7780]433
[14073]434        default:
435          cat_message(header, body);
436          break;
437        }
[7780]438
[14073]439      free(body);
440      msgs++;
[7780]441    }
[14073]442
443  free(header);
444  free(subject);
[7780]445}
446
[14069]447int main(int argc, char **argv)
[7780]448{
[14857]449  char *whoami, *message, *server = NULL;
[14073]450  int method = LERT_CAT, no_more = FALSE;
[14857]451  char **arg = argv;
[7780]452
[14073]453  whoami = strrchr(argv[0], '/');
454  if (whoami)
455    whoami++;
456  else
457    whoami = argv[0];
[7780]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   */
[14069]464
[14857]465  while (++arg - argv < argc)
[14069]466    {
[14857]467      if (**arg == '-')
[14069]468        {
[14857]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            }
[14069]492        }
493    }
494  /* Get the server's string for this user. */
[14857]495  message = lert_says(no_more, server);
[14073]496  if (message)
497    {
[14081]498      view_message(message, method, no_more);
[14073]499      free(message);
500    }
[7780]501
[14069]502  return 0;
[7780]503}
Note: See TracBrowser for help on using the repository browser.