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

Revision 10378, 16.3 KB checked in by danw, 27 years ago (diff)
only send the useful part of the kerberos authenticator. (cuts packet size down by an order of magnitude)
Line 
1/*
2    file: lert.c
3    main bungler: Mike Barker <mbarker@mit.edu>
4    started Dec. 2, 1994
5    loosely based on Jeff Schiller's checkmyid program and
6    the Athena get_message code
7    client-server with Kerberos authentication
8
9    version 1:  put details in a couple of subroutines
10                a) call server and get magic letter
11                b) use letter to pick a file and send to user
12
13        man page
14
15    later: add user .lertrc file to let user tailor processing
16           e.g. frequency (daily, every login, etc.)
17                method of display (zephyr, email, more,...)
18           do some administrative side processing--let the administrator
19                know who has been notified and so forth.
20
21modified 1/11/95
22    change timeouts/retries for 5 second
23    -q quiet switch
24    fallback after zephyr fails to cat...
25
26modified 1/25/95
27  mandatory
28    check system returns and print error if fails
29  niceties
30    no_error variable name backwards -- if (err_messages)
31
32modified 1/26/95
33    replace bzero with memset
34    replace bcopy with memmove
35    replace include strings.h with string.h
36    replace syserrlist with strerror
37    change strrchr to buffer + strlen
38    put subject line of mail message in a file (allow configuration)
39    use isalnum to avoid bad returned characters
40    cleanup printing code (really ugly!) somewhat improved
41
42pending fixes 1/26/95
43    use mk_priv instead of mk_auth to protect -n flag
44
45*/
46
47#include <unistd.h>
48#include <stdlib.h>
49#include <stdio.h>
50#include <krb.h>
51#include <des.h>
52#include <sys/types.h>
53#include <sys/stat.h>
54#ifdef _AIX
55#include <sys/select.h>
56#endif
57#include <fcntl.h>
58#include <sys/socket.h>
59#include <ndbm.h>
60#include <netinet/in.h>
61#include <netdb.h>
62#include <sys/time.h>
63#include <hesiod.h>
64#include <string.h>
65#include <pwd.h>
66#include <errno.h>
67#include "lert.h"
68
69/*
70  bad coding practice and not currently needed
71extern int errno;
72extern char *sys_errlist[];
73
74use #include <errno.h> and strerror(errno) instead...
75
76 */
77
78static struct timeval timeout = { LERT_TIMEOUT, 0 };
79
80/*
81  very bad coding practice, but it gets the job done fast
82 */
83static int error_messages = TRUE;
84
85void Usage(pname, errname)
86     char *pname, *errname;
87{
88  fprintf(stderr,
89          "%s <%s>: Usage: %s [-zephyr|-z] [-mail|-m] [-no|-n] [-quiet|-q]\n",
90          pname, errname, pname);
91}
92
93lert_says(get_lerts_blessing, no_p)
94char * get_lerts_blessing;
95int no_p;
96{
97  KTEXT_ST authent;
98/*
99  KTEXT_ST private;
100 */
101  unsigned char packet[2048];
102  unsigned char ipacket[2048];
103  u_long iplen;
104  int biplen;
105  int gotit;
106  struct hostent *hp;
107  struct sockaddr_in sin, lsin;
108  fd_set readfds;
109  int i;
110  char *ip;
111  char **tip;
112  int trys;
113  char *krb_get_phost();
114  char srealm[REALM_SZ];
115  char sname[SNAME_SZ];
116  char sinst[INST_SZ];
117  char *cp;
118  Key_schedule sched;
119  CREDENTIALS cred;
120  int plen;
121  int status;
122  int s;
123  u_long checksum_sent;
124  u_long checksum_rcv;
125  MSG_DAT msg_data;
126  char hostname[256];
127
128  void bombout();
129
130  /*
131    find out where lert lives
132
133    note the presumption that there is only one lert!
134   */
135
136  tip = (hes_resolve(LERT_SERVER, LERT_TYPE));
137  if (tip == NULL){
138    /*
139      No Hesiod available
140      fall into hardcoded--below for realmofhost
141     */
142    ip = LERT_HOME;
143  } else {
144    ip = tip[0];
145  }
146
147  /*
148    now begin the real kerberos code
149    find out which realm the server is in
150    and the host name
151    hardcode the service name
152    and make a request
153   */
154
155  cp = (char *)krb_realmofhost(ip);
156  if (cp == NULL) bombout(ERR_KERB_REALM);
157  strcpy(srealm, cp);
158
159  /*
160    get hostname alias
161   */
162  cp = krb_get_phost(ip);
163  if (cp == NULL) bombout(ERR_KERB_PHOST);
164  strcpy(sinst, cp);
165
166  /*
167    why isn't this the same service we asked hesiod to resolve?
168   */
169  strcpy(sname, LERT_SERVICE);
170
171  /*
172     int krb_mk_req(authent,service,instance,realm,checksum)
173     KTEXT authent;
174     char *service;
175     char *instance;
176     char *realm;
177     u_long checksum;
178
179   need to check exactly what format the checksum is...
180
181   note: since we use kerberos private message, we don't
182   really need the checksum code anymore...
183
184   */
185
186  checksum_sent = (u_long) no_p;
187
188  status = krb_mk_req(&authent, sname, sinst, srealm, checksum_sent);
189  if (status != KSUCCESS) bombout(ERR_KERB_AUTH);
190
191  /*
192     now do your client-server exchange
193   */
194
195  /*
196    zero out the packets
197   */
198  memset(packet, 0, sizeof(packet));
199  memset(ipacket, 0, sizeof(ipacket));
200
201  hp = gethostbyname(ip);
202
203  if (hp == NULL) bombout(ERR_HOSTNAME);
204  memset(&sin, 0, sizeof(sin));
205  sin.sin_family = hp->h_addrtype;
206  memcpy((char *)&sin.sin_addr, (char *)hp->h_addr,
207               sizeof(hp->h_addr));
208  sin.sin_port = htons(LERT_PORT);
209
210  /*
211   lert's basic protocol
212   client send version, one byte query code, and authentication
213  */
214  packet[0] = LERT_VERSION;
215  packet[1] = no_p;
216  memcpy((char *) &packet[LERT_LENGTH], (char *)&authent,
217         sizeof(int) + authent.length);
218  plen = LERT_LENGTH + sizeof(int) + authent.length;
219
220  s = socket(AF_INET, SOCK_DGRAM, 0);
221  if (s < 0) bombout(ERR_SOCKET);
222  if (connect(s, (struct sockaddr *)&sin, sizeof(struct sockaddr)) < 0) bombout(ERR_CONNECT);
223  trys = RETRIES;
224  gotit = 0;
225  while (trys > 0) {
226    if (send(s, packet, plen, 0) < 0) bombout(ERR_SEND);
227    FD_ZERO(&readfds);
228    FD_SET(s, &readfds);
229    if ((select(s+1, &readfds, (fd_set *) 0, (fd_set *)0, &timeout) < 1)
230        || !FD_ISSET(s, &readfds)) {
231      trys--;
232      continue;
233    }
234    /*
235      use "recvfrom" so we know client's address
236      */
237/*
238    biplen = sizeof(struct sockaddr);
239    iplen = recvfrom(s, (char *)ipacket, 2048, 0,
240                     (struct sockaddr *)&sin, &biplen);
241 */
242    iplen = recv(s, (char *)ipacket, 2048, 0);
243
244    if (iplen < 0) bombout(ERR_RCV);
245    gotit++;
246    break;
247  }
248
249  /*
250    Get my address
251   */
252  memset((char *) &lsin, 0, sizeof(lsin));
253  i = sizeof(lsin);
254  if (getsockname(s, (struct sockaddr *)&lsin, &i) < 0) bombout(LERT_NO_SOCK);
255
256  gethostname(hostname, sizeof(hostname));
257  hp = gethostbyname(hostname);
258  memcpy((char *) &lsin.sin_addr.s_addr, hp->h_addr, hp->h_length);
259 
260  shutdown (s, 2);
261  close(s);
262  if (!gotit) bombout(ERR_TIMEOUT);
263
264  /*
265    int krb_get_cred(service,instance,realm,c)
266    char *service;
267    char *instance;
268    char *realm;
269    CREDENTIALS *c;
270
271    struct credentials {
272    char    service[ANAME_SZ];   Service name
273    char    instance[INST_SZ];   Instance
274    char    realm[REALM_SZ];     Auth domain
275    C_Block session;             Session key
276    int     lifetime;            Lifetime
277    int     kvno;                Key version number
278    KTEXT_ST ticket_st;          The ticket itself
279    long    issue_date;          The issue time
280    char    pname[ANAME_SZ];     Principal's name
281    char    pinst[INST_SZ];      Principal's instance
282    };
283
284    */
285
286  status = krb_get_cred(sname, sinst, srealm, &cred);
287  if (status != KSUCCESS) bombout(ERR_KERB_CRED);
288
289  /*
290
291    somehow, I don't think I should be doing this, but...
292
293    need key schedule for session key
294
295    */
296  des_key_sched(cred.session, sched);
297
298  /*
299
300NOTE: not currently implemented...but I'm retaining the comment and
301line of code from the cmi program in case.
302
303    The bcopy below is necessary so that on the DECstation the private
304    message is word aligned 
305  (void) bcopy((char *)ipacket, (char *) private.dat, iplen);
306    */
307
308  /*
309
310    long krb_rd_priv(in,in_length,schedule,key,sender,receiver,msg_data)
311    u_char *in;
312    u_long in_length;
313    Key_schedule schedule;
314    des_cblock key;
315    struct sockaddr_in *sender;
316    struct sockaddr_in *receiver;
317    MSG_DAT *msg_data;
318
319    struct msg_dat {
320    unsigned char *app_data;     pointer to appl data
321    unsigned long app_length;    length of appl data
322    unsigned long hash;          hash to lookup replay
323    int     swap;                swap bytes?
324    long    time_sec;            msg timestamp seconds
325    unsigned char time_5ms;      msg timestamp 5ms units
326    };
327
328    LARGE RED WARNING NOTE!!!!
329
330    the two parameters below (lsin and lsin) are supposed
331    to be the sender and receiver--lsin and sin.
332
333    kerberos.p9 has some odd code that messes with the
334    time based on the relationship of the addresses (direction bit?)
335
336    it doesn't seem to care (at present) whether the second one
337    is a lie or not, and doing so cleared up a bug.
338
339    i.e. this is an ugly, good-for-nothing workaround.  don't
340    copy this code, get the library fixed.
341
342    and if you get RD_AP_TIME (37) error returns and can't figure
343    out why, try this...
344
345    ugly, ugly, ugly.  don't do this in your own house.  don't
346    attempt it with professionals watching, either, cause you'll
347    get your hand slapped!
348
349    but it does work.
350
351    EVEN BIGGER RED NOTE!!!!
352
353    ignore previous stupid note.  in reality (which has little to
354    do with kerberos) you can put a lot of junk in.  however, try
355    mk_priv -- local, remote  (in server for my appl)
356    rd_priv -- remote, local  (here in client for my appl)
357
358    make sure you set them up before use--watch for 0's in gdb
359
360    and! voila!  c'est omelette (lots of broken eggs...)
361
362    */
363
364  status = krb_rd_priv(ipacket,
365                       iplen,
366                       sched,
367                       cred.session,
368                       &sin,
369                       &lsin,
370                       &msg_data);
371  if (status) bombout(ERR_SERVER);
372
373  if (msg_data.app_length == 0) return(LERT_NOT_IN_DB);
374  /*
375    at this point, we have a packet.  check it out
376    [0] LERT_VERSION
377    [1] code response
378    [2 on] data...
379
380    server responds with version, one byte response, and authentication
381   
382    bump to version, one byte response,
383    plus KTEXT format data -- length, data...
384
385    */
386  if (msg_data.app_data[0] != LERT_VERSION) bombout(ERR_VERSION);
387  if (msg_data.app_data[1] == LERT_MSG) {
388    /*
389      apparently we have a message from the server...
390      set it up, terminate the string, and do it!
391      */
392
393    memcpy(get_lerts_blessing,
394           &(msg_data.app_data[LERT_CHECK]),
395           msg_data.app_length - LERT_CHECK);
396/*
397  should already be in packet
398    get_lerts_blessing[msg_data.app_length - LERT_CHECK] = '\0';
399 */
400    return(LERT_GOTCHA);
401  } else {
402    /*
403      better safe than sorry
404      */
405    get_lerts_blessing[0] = '\0';
406  }
407  return(LERT_NOT_IN_DB);
408}
409
410/*
411    general error reporting
412 */
413 
414void bombout(mess)
415int mess;
416{
417  if (error_messages) {
418    fprintf(stderr, "lert: ");
419    switch(mess)
420      {
421      case ERR_KERB_CRED:
422        fprintf(stderr, "Error getting kerberos credentials.\n");
423        break;
424      case ERR_KERB_AUTH:
425        fprintf(stderr, "Error getting kerberos authentication.\n");
426        fprintf(stderr, "Are your tickets valid?\n");
427        break;
428      case ERR_TIMEOUT:
429        fprintf(stderr, "Timed out waiting for response from server.\n");
430        break;
431      case ERR_SERVER:
432        fprintf(stderr, "Unable to contact server.\n");
433        break;
434      case ERR_SERVED:
435        fprintf(stderr, "Bad string from server.\n");
436        break;
437      case ERR_SEND:
438        fprintf(stderr, "Error in send: %s\n", strerror(errno));
439        break;
440      case ERR_RCV:
441        fprintf(stderr, "Error in recv: %s\n", strerror(errno));
442        break;
443      case ERR_USER:
444        fprintf(stderr, "Could not get your name to send messages\n");
445        break;
446      case NO_PROCS:
447        fprintf(stderr, "Error running child processes: %s\n",
448                strerror(errno));
449        break;
450      case ERR_MEMORY:
451        fprintf(stderr, "Out of memory\n");
452        break;
453      default:
454        fprintf(stderr, "A problem (%d) occurred when checking the database\n",
455                mess);
456        break;
457      }
458    fprintf(stderr, "Please try again later.\n");
459    fprintf(stderr,
460            "If this problem persists, please report it to a consultant.\n");
461    }
462  exit (1);
463}
464
465/*
466  programming note: this routine mallocs!  if used repeatedly, make
467  sure you free the memory!
468 */
469
470char * get_lert_sub()
471{
472  FILE * myfile;
473  char buffer[250];
474  char * turkey;
475  char * mugwump;
476
477  strcpy(buffer, LERTS_DEF_SUBJECT);
478
479  myfile = fopen(LERTS_MSG_SUBJECT, "r");
480  if (myfile != NULL) {
481    turkey = fgets(buffer, 250, myfile);
482    if (turkey == NULL) strcpy(buffer, LERTS_DEF_SUBJECT);
483    fclose(myfile);
484    }
485  /*
486    take care of the line feed read by fgets
487    */
488  mugwump = strchr(buffer, '\n');
489  if (mugwump != NULL)
490    *mugwump = '\0';
491  /*
492    now make a buffer for the subject
493    */
494  mugwump = (char *)malloc(strlen(buffer) + 1);
495  if (mugwump == NULL) bombout(ERR_MEMORY);
496  strcpy(mugwump, buffer);
497  return(mugwump);
498}
499
500/*
501  currently supports cat, zephyr, and email messaging...
502  why?  because it was there!
503  :-)
504 */
505
506void view_message(message, type)
507char * message;
508int type;
509{
510  char *whoami, *getlogin();
511  char *ptr;
512  char * tail;
513  char * type_buffer;
514  char buffer[512];
515  struct passwd *pw;
516  int status;
517
518/*
519  these buffers permit centralized handling of the
520  various options.  note that both z and m require
521  on the fly construction (at least user name) so
522  buffers are kept roomy...
523 */
524
525  char zprog[128];
526  char mprog[512];
527  char cprog[4];
528
529  strcpy(zprog, "zwrite -q ");
530  strcpy(mprog, "mhmail ");
531  strcpy(cprog, "cat");
532
533  whoami = getenv("USER");
534 
535  if(!whoami)
536    whoami = getlogin();
537
538  if(!whoami) {
539    pw = getpwuid(getuid());
540    if(pw) {
541      whoami = pw->pw_name;
542    } else {
543      bombout(ERR_USER);
544    }
545  }
546  ptr = message;
547
548  while(*ptr) {
549    switch (type) {
550    case LERT_Z:
551      /*
552        zwrite -q whoami
553        */
554      type_buffer = zprog;
555      strcat(type_buffer, whoami);
556      type = LERT_HANDLE;
557      break;
558
559    case LERT_MAIL:
560      /*
561        cat ... | mhmail whoami -subject "lert's msg"
562        */
563      type_buffer = mprog;
564/*
565      strcpy(mprog, "mhmail ");
566 */
567      strcat(type_buffer, whoami);
568      strcat(type_buffer, " -subject \"");
569      strcat(type_buffer, get_lert_sub());
570      strcat(type_buffer, "\" ");
571      type = LERT_HANDLE;
572      break;
573
574    case LERT_HANDLE:
575      /*
576        take care of the case where there is one lonely message
577        just combine the two (lert0 and lertx) for the zwrite
578        hope the writer of the messages realizes they will be
579        jammed together...
580        */
581      if (strlen(ptr) == 1) {
582        strcpy(buffer, "cat ");
583        strcat(buffer, LERTS_MSG_FILES);
584        strcat(buffer, "0 ");
585        strcat(buffer, LERTS_MSG_FILES);
586        if (isalnum(*ptr))
587          strcat(buffer, ptr);
588        else
589          bombout(ERR_SERVED);
590        strcat(buffer, " | ");
591        strcat(buffer, type_buffer);
592        status = system(buffer);
593        if (status) {
594          /*
595            bad news
596            if zwrite or email failed
597            try cat!  lazy code...
598            */
599          if (type_buffer != cprog)
600            type = LERT_CAT;
601          else
602            bombout(NO_PROCS);
603          break;
604        }
605        ptr++;
606      } else {
607        /*
608          do multiple message notification
609          */
610        strcpy(buffer, type_buffer);
611        strcat(buffer, " < ");
612        strcat(buffer, LERTS_MSG_FILES);
613        /*
614          give ourselves one more space to deal with
615          */
616        tail = buffer + strlen(buffer);
617
618        *(tail+1) = '\0';
619        /*
620          always start with the lert0
621          */
622        *tail = '0';
623        status = system(buffer);
624        if (status) {
625          /*
626            bad news-- zwrite or email  failed
627            try cat!  lazy code...
628            */
629          if (type_buffer != cprog)
630            type = LERT_CAT;
631          else
632            bombout(NO_PROCS);
633          break;
634        }
635   
636        /*
637          then step through the rest of the messages for this user
638          */
639        while (*ptr) {
640          if (isalnum(*ptr))
641            *tail = *ptr;
642          else
643            bombout(ERR_SERVED);
644          status = system(buffer);
645          if (status) bombout(NO_PROCS);
646          ptr++;
647        }
648      }
649      break;
650
651    case LERT_CAT:
652    default:
653      /*
654        cat notification
655        */
656      type_buffer = cprog;
657      type = LERT_HANDLE;
658      break;
659
660      }
661    }
662}
663
664main(argc, argv)
665int argc;
666char ** argv;
667{
668  char buffer[512];
669  char *get_lerts_blessing;
670  char ** xargv = argv;
671  int xargc = argc;
672  int zephyr_p = LERT_CAT, no_p = FALSE;
673  int result;
674
675  get_lerts_blessing = buffer;
676
677  /* Argument Processing:
678   *    -z or -zephyr: send the message as a zephyrgram.
679   *    -m or -mail: send the message as email
680   *    -n or -no: no more messages!
681   * note: why not use one of the argument processors?
682   */
683  if(argc>3) {
684    Usage(argv[0], "too many arguments");
685    exit(1);
686  }
687  /* note: initialized in declaration */
688  while(--xargc) {
689    xargv++;
690    if((!strcmp(xargv[0],"-zephyr"))||(!strcmp(xargv[0],"-z"))) {
691      zephyr_p = LERT_Z;
692    } else if((!strcmp(xargv[0],"-mail"))||(!strcmp(xargv[0],"-m"))) {
693      zephyr_p = LERT_MAIL;
694    } else if((!strcmp(xargv[0],"-no"))||(!strcmp(xargv[0],"-n"))) {
695      no_p = TRUE;
696    } else if((!strcmp(xargv[0],"-quiet"))||(!strcmp(xargv[0],"-q"))) {
697      error_messages = FALSE;
698    } else {
699      Usage(argv[0], xargv[0]);
700      exit(1);
701    }     
702  }
703
704  /*
705    get the server's string for this user
706   */
707  result = lert_says(get_lerts_blessing, no_p);
708
709  if (result == LERT_GOTCHA) {
710    /*
711      pass on the selection to the user
712      */
713    view_message(get_lerts_blessing, zephyr_p);
714  }
715  exit(0);
716}
717
718
719
720
721
Note: See TracBrowser for help on using the repository browser.