README revision 95154
1117259SacheThis directory contains the source files for libmilter. 2117259Sache 3117259SacheThe sendmail Mail Filter API (Milter) is designed to allow third-party 4117259Sacheprograms access to mail messages as they are being processed in order to 5117259Sachefilter meta-information and content. 6117259Sache 7117259SacheThis README file describes the steps needed to compile and run a filter, 8117259Sachethrough reference to a sample filter which is attached at the end of this 9117259Sachefile. It is necessary to first build libmilter.a, which can be done by 10117259Sacheissuing the './Build' command in SRCDIR/libmilter . 11117259Sache 12117259SacheNOTE: If you intend to use filters in sendmail, you must compile sendmail 13117259Sachewith -DMILTER defined. You can do this by adding the following to 14117259Sacheyour devtools/Site/site.config.m4 file: 15117259Sache 16117259Sache APPENDDEF(`conf_sendmail_ENVDEF', `-DMILTER') 17117259Sache 18117259Sache+----------------+ 19117259Sache| SECURITY HINTS | 20117259Sache+----------------+ 21117259Sache 22117259SacheNote: we strongly recommend not to run any milter as root. Libmilter 23117259Sachedoes not need root access to communicate with sendmail. It is a 24117259Sachegood security practice to run a program only with root privileges 25117259Sacheif really necessary. A milter should probably check first whether 26117259Sacheit runs as root and refuse to start in that case. There is a 27117259Sachecompile time option _FFR_MILTER_ROOT_UNSAFE which keeps libmilter 28117259Sachefrom unlinking a socket when running as root. It is recommended 29117259Sacheto turn on this option: 30117259Sache 31117259Sache APPENDDEF(`conf_libmilter_ENVDEF', `-D_FFR_MILTER_ROOT_UNSAFE ') 32117259Sache 33117259Sache 34117259Sache+-------------------+ 35117259Sache| BUILDING A FILTER | 36117259Sache+-------------------+ 37117259Sache 38117259SacheThe following command presumes that the sample code from the end of this 39117259SacheREADME is saved to a file named 'sample.c' and built in the local platform- 40117259Sachespecific build subdirectory (SRCDIR/obj.*/libmilter). 41117259Sache 42117259Sache cc -I../../sendmail -I../../include -o sample sample.c libmilter.a ../libsm/libsm.a -pthread 43117259Sache 44117259SacheIt is recommended that you build your filters in a location outside of 45117259Sachethe sendmail source tree. Modify the compiler include references (-I) 46117259Sacheand the library locations accordingly. Also, some operating systems may 47117259Sacherequire additional libraries. For example, SunOS 5.X requires '-lresolv 48117259Sache-lsocket -lnsl'. Depending on your operating system you may need a library 49117259Sacheinstead of the option -pthread, e.g., -lpthread. 50117259Sache 51117259SacheFilters must be thread-safe! Many operating systems now provide support for 52117259SachePOSIX threads in the standard C libraries. The compiler flag to link with 53117259Sachethreading support differs according to the compiler and linker used. Check 54117259Sachethe Makefile in your appropriate obj.*/libmilter build subdirectory if you 55117259Sacheare unsure of the local flag used. 56117259Sache 57117259SacheNote that since filters use threads, it may be necessary to alter per 58117259Sacheprocess limits in your filter. For example, you might look at using 59117259Sachesetrlimit() to increase the number of open file descriptors if your filter 60117259Sacheis going to be busy. 61117259Sache 62117259Sache 63117259Sache+----------------------------------------+ 64117259Sache| SPECIFYING FILTERS IN SENDMAIL CONFIGS | 65117259Sache+----------------------------------------+ 66117259Sache 67117259SacheFilters are specified with a key letter ``X'' (for ``eXternal''). 68117259Sache 69117259SacheFor example: 70117259Sache 71117259Sache Xfilter1, S=local:/var/run/f1.sock, F=R 72117259Sache Xfilter2, S=inet6:999@localhost, F=T, T=C:10m;S:1s;R:1s;E:5m 73117259Sache Xfilter3, S=inet:3333@localhost 74117259Sache 75117259Sachespecifies three filters. Filters can be specified in your .mc file using 76117259Sachethe following: 77117259Sache 78117259Sache INPUT_MAIL_FILTER(`filter1', `S=local:/var/run/f1.sock, F=R') 79117259Sache INPUT_MAIL_FILTER(`filter2', `S=inet6:999@localhost, F=T, T=C:10m;S:1s;R:1s;E:5m') 80117259Sache INPUT_MAIL_FILTER(`filter3', `S=inet:3333@localhost') 81117259Sache 82117259SacheThe first attaches to a Unix-domain socket in the /var/run directory; the 83117259Sachesecond uses an IPv6 socket on port 999 of localhost, and the third uses an 84117259SacheIPv4 socket on port 3333 of localhost. The current flags (F=) are: 85117259Sache 86117259Sache R Reject connection if filter unavailable 87117259Sache T Temporary fail connection if filter unavailable 88117259Sache 89117259SacheIf neither F=R nor F=T is specified, the message is passed through sendmail 90117259Sachein case of filter errors as if the failing filters were not present. 91117259Sache 92117259SacheFinally, you can override the default timeouts used by sendmail when 93117259Sachetalking to the filters using the T= equate. There are four fields inside 94117259Sacheof the T= equate: 95117259Sache 96117259SacheLetter Meaning 97117259Sache C Timeout for connecting to a filter (if 0, use system timeout) 98117259Sache S Timeout for sending information from the MTA to a filter 99117259Sache R Timeout for reading reply from the filter 100117259Sache E Overall timeout between sending end-of-message to filter 101117259Sache and waiting for the final acknowledgment 102117259Sache 103117259SacheNote the separator between each is a ';' as a ',' already separates equates 104117259Sacheand therefore can't separate timeouts. The default values (if not set in 105117259Sachethe config) are: 106117259Sache 107117259SacheT=C:5m;S:10s;R:10s;E:5m 108117259Sache 109117259Sachewhere 's' is seconds and 'm' is minutes. 110117259Sache 111117259SacheWhich filters are invoked and their sequencing is handled by the 112117259SacheInputMailFilters option. Note: if InputMailFilters is not defined no filters 113117259Sachewill be used. 114117259Sache 115117259Sache O InputMailFilters=filter1, filter2, filter3 116117259Sache 117117259SacheThis is is set automatically according to the order of the 118117259SacheINPUT_MAIL_FILTER commands in your .mc file. Alternatively, you can 119117259Sachereset its value by setting confINPUT_MAIL_FILTERS in your .mc file. 120117259SacheThis options causes the three filters to be called in the same order 121117259Sachethey were specified. It allows for possible future filtering on output 122117259Sache(although this is not intended for this release). 123117259Sache 124117259SacheAlso note that a filter can be defined without adding it to the input 125117259Sachefilter list by using MAIL_FILTER() instead of INPUT_MAIL_FILTER() in your 126117259Sache.mc file. 127117259Sache 128117259SacheTo test sendmail with the sample filter, the following might be added (in 129117259Sachethe appropriate locations) to your .mc file: 130117259Sache 131117259Sache INPUT_MAIL_FILTER(`sample', `S=local:/var/run/f1.sock') 132117259Sache 133117259Sache 134117259Sache+------------------+ 135117259Sache| TESTING A FILTER | 136117259Sache+------------------+ 137117259Sache 138117259SacheOnce you have compiled a filter, modified your .mc file and restarted 139117259Sachethe sendmail process, you will want to test that the filter performs as 140117259Sacheintended. 141117259Sache 142117259SacheThe sample filter takes one argument -p, which indicates the local port 143117259Sacheon which to create a listening socket for the filter. Maintaining 144117259Sacheconsistency with the suggested options for sendmail.cf, this would be the 145117259SacheUNIX domain socket located in /var/run/f1.sock. 146117259Sache 147117259Sache % ./sample -p local:/var/run/f1.sock 148117259Sache 149117259SacheIf the sample filter returns immediately to a command line, there was either 150117259Sachean error with your command or a problem creating the specified socket. 151117259SacheFurther logging can be captured through the syslogd daemon. Using the 152117259Sache'netstat -a' command can ensure that your filter process is listening on 153117259Sachethe appropriate local socket. 154117259Sache 155117259SacheEmail messages must be injected via SMTP to be filtered. There are two 156117259Sachesimple means of doing this; either using the 'sendmail -bs' command, or 157117259Sacheby telnetting to port 25 of the machine configured for milter. Once 158117259Sacheconnected via one of these options, the session can be continued through 159117259Sachethe use of standard SMTP commands. 160117259Sache 161117259Sache% sendmail -bs 162117259Sache220 test.sendmail.com ESMTP Sendmail 8.11.0/8.11.0; Tue, 10 Nov 1970 13:05:23 -0500 (EST) 163117259SacheHELO localhost 164117259Sache250 test.sendmail.com Hello testy@localhost, pleased to meet you 165117259SacheMAIL From:<testy> 166117259Sache250 2.1.0 <testy>... Sender ok 167117259SacheRCPT To:<root> 168117259Sache250 2.1.5 <root>... Recipient ok 169117259SacheDATA 170117259Sache354 Enter mail, end with "." on a line by itself 171117259SacheFrom: testy@test.sendmail.com 172117259SacheTo: root@test.sendmail.com 173117259SacheSubject: testing sample filter 174117259Sache 175117259SacheSample body 176117259Sache. 177117259Sache250 2.0.0 dB73Zxi25236 Message accepted for delivery 178117259SacheQUIT 179117259Sache221 2.0.0 test.sendmail.com closing connection 180117259Sache 181117259SacheIn the above example, the lines beginning with numbers are output by the 182117259Sachemail server, and those without are your input. If everything is working 183117259Sacheproperly, you will find a file in /tmp by the name of msg.XXXXXXXX (where 184117259Sachethe Xs represent any combination of letters and numbers). This file should 185117259Sachecontain the message body and headers from the test email entered above. 186117259Sache 187117259SacheIf the sample filter did not log your test email, there are a number of 188117259Sachemethods to narrow down the source of the problem. Check your system 189117259Sachelogs written by syslogd and see if there are any pertinent lines. You 190117259Sachemay need to reconfigure syslogd to capture all relevant data. Additionally, 191117259Sachethe logging level of sendmail can be raised with the LogLevel option. 192117259SacheSee the sendmail(8) manual page for more information. 193117259Sache 194117259Sache 195117259Sache+--------------+ 196117259Sache| REQUIREMENTS | 197117259Sache+--------------+ 198117259Sache 199117259Sachelibmilter requires pthread support in the operating system. Moreover, it 200117259Sacherequires that the library functions it uses are thread safe; which is true 201for the operating systems libmilter has been developed and tested on. On 202some operating systems this requires special compile time options (e.g., 203not just -pthread). libmilter is currently known to work on (modulo problems 204in the pthread support of some specific versions): 205 206FreeBSD 3.x, 4.x 207SunOS 5.x (x >= 5) 208AIX 4.3.x 209HP UX 11.x 210Linux (recent versions/distributions) 211 212libmilter is currently not supported on: 213 214IRIX 6.x 215Ultrix 216 217Feedback about problems (and possible fixes) is welcome. 218 219+--------------------------+ 220| SOURCE FOR SAMPLE FILTER | 221+--------------------------+ 222 223Note that the filter below may not be thread safe on some operating 224systems. You should check your system man pages for the functions used 225below to verify the functions are thread safe. 226 227/* A trivial filter that logs all email to a file. */ 228 229#include <sys/types.h> 230#include <stdio.h> 231#include <stdlib.h> 232#include <string.h> 233#include <sysexits.h> 234#include <unistd.h> 235 236#include "libmilter/mfapi.h" 237 238#ifndef true 239typedef int bool; 240# define false 0 241# define true 1 242#endif /* ! true */ 243 244struct mlfiPriv 245{ 246 char *mlfi_fname; 247 FILE *mlfi_fp; 248}; 249 250#define MLFIPRIV ((struct mlfiPriv *) smfi_getpriv(ctx)) 251 252extern sfsistat mlfi_cleanup(SMFICTX *, bool); 253 254sfsistat 255mlfi_envfrom(ctx, envfrom) 256 SMFICTX *ctx; 257 char **envfrom; 258{ 259 struct mlfiPriv *priv; 260 int fd = -1; 261 262 /* allocate some private memory */ 263 priv = malloc(sizeof *priv); 264 if (priv == NULL) 265 { 266 /* can't accept this message right now */ 267 return SMFIS_TEMPFAIL; 268 } 269 memset(priv, '\0', sizeof *priv); 270 271 /* open a file to store this message */ 272 priv->mlfi_fname = strdup("/tmp/msg.XXXXXXXX"); 273 if (priv->mlfi_fname == NULL) 274 { 275 free(priv); 276 return SMFIS_TEMPFAIL; 277 } 278 if ((fd = mkstemp(priv->mlfi_fname)) < 0 || 279 (priv->mlfi_fp = fdopen(fd, "w+")) == NULL) 280 { 281 if (fd >= 0) 282 (void) close(fd); 283 free(priv->mlfi_fname); 284 free(priv); 285 return SMFIS_TEMPFAIL; 286 } 287 288 /* save the private data */ 289 smfi_setpriv(ctx, priv); 290 291 /* continue processing */ 292 return SMFIS_CONTINUE; 293} 294 295sfsistat 296mlfi_header(ctx, headerf, headerv) 297 SMFICTX *ctx; 298 char *headerf; 299 char *headerv; 300{ 301 /* write the header to the log file */ 302 fprintf(MLFIPRIV->mlfi_fp, "%s: %s\r\n", headerf, headerv); 303 304 /* continue processing */ 305 return SMFIS_CONTINUE; 306} 307 308sfsistat 309mlfi_eoh(ctx) 310 SMFICTX *ctx; 311{ 312 /* output the blank line between the header and the body */ 313 fprintf(MLFIPRIV->mlfi_fp, "\r\n"); 314 315 /* continue processing */ 316 return SMFIS_CONTINUE; 317} 318 319sfsistat 320mlfi_body(ctx, bodyp, bodylen) 321 SMFICTX *ctx; 322 u_char *bodyp; 323 size_t bodylen; 324{ 325 /* output body block to log file */ 326 if (fwrite(bodyp, bodylen, 1, MLFIPRIV->mlfi_fp) <= 0) 327 { 328 /* write failed */ 329 (void) mlfi_cleanup(ctx, false); 330 return SMFIS_TEMPFAIL; 331 } 332 333 /* continue processing */ 334 return SMFIS_CONTINUE; 335} 336 337sfsistat 338mlfi_eom(ctx) 339 SMFICTX *ctx; 340{ 341 return mlfi_cleanup(ctx, true); 342} 343 344sfsistat 345mlfi_close(ctx) 346 SMFICTX *ctx; 347{ 348 return SMFIS_ACCEPT; 349} 350 351sfsistat 352mlfi_abort(ctx) 353 SMFICTX *ctx; 354{ 355 return mlfi_cleanup(ctx, false); 356} 357 358sfsistat 359mlfi_cleanup(ctx, ok) 360 SMFICTX *ctx; 361 bool ok; 362{ 363 sfsistat rstat = SMFIS_CONTINUE; 364 struct mlfiPriv *priv = MLFIPRIV; 365 char *p; 366 char host[512]; 367 char hbuf[1024]; 368 369 if (priv == NULL) 370 return rstat; 371 372 /* close the archive file */ 373 if (priv->mlfi_fp != NULL && fclose(priv->mlfi_fp) == EOF) 374 { 375 /* failed; we have to wait until later */ 376 rstat = SMFIS_TEMPFAIL; 377 (void) unlink(priv->mlfi_fname); 378 } 379 else if (ok) 380 { 381 /* add a header to the message announcing our presence */ 382 if (gethostname(host, sizeof host) < 0) 383 snprintf(host, sizeof host, "localhost"); 384 p = strrchr(priv->mlfi_fname, '/'); 385 if (p == NULL) 386 p = priv->mlfi_fname; 387 else 388 p++; 389 snprintf(hbuf, sizeof hbuf, "%s@%s", p, host); 390 smfi_addheader(ctx, "X-Archived", hbuf); 391 } 392 else 393 { 394 /* message was aborted -- delete the archive file */ 395 (void) unlink(priv->mlfi_fname); 396 } 397 398 /* release private memory */ 399 free(priv->mlfi_fname); 400 free(priv); 401 smfi_setpriv(ctx, NULL); 402 403 /* return status */ 404 return rstat; 405} 406 407struct smfiDesc smfilter = 408{ 409 "SampleFilter", /* filter name */ 410 SMFI_VERSION, /* version code -- do not change */ 411 SMFIF_ADDHDRS, /* flags */ 412 NULL, /* connection info filter */ 413 NULL, /* SMTP HELO command filter */ 414 mlfi_envfrom, /* envelope sender filter */ 415 NULL, /* envelope recipient filter */ 416 mlfi_header, /* header filter */ 417 mlfi_eoh, /* end of header */ 418 mlfi_body, /* body block filter */ 419 mlfi_eom, /* end of message */ 420 mlfi_abort, /* message aborted */ 421 mlfi_close /* connection cleanup */ 422}; 423 424 425int 426main(argc, argv) 427 int argc; 428 char *argv[]; 429{ 430 int c; 431 const char *args = "p:"; 432 433 /* Process command line options */ 434 while ((c = getopt(argc, argv, args)) != -1) 435 { 436 switch (c) 437 { 438 case 'p': 439 if (optarg == NULL || *optarg == '\0') 440 { 441 (void) fprintf(stderr, "Illegal conn: %s\n", 442 optarg); 443 exit(EX_USAGE); 444 } 445 (void) smfi_setconn(optarg); 446 break; 447 448 } 449 } 450 if (smfi_register(smfilter) == MI_FAILURE) 451 { 452 fprintf(stderr, "smfi_register failed\n"); 453 exit(EX_UNAVAILABLE); 454 } 455 return smfi_main(); 456} 457 458/* eof */ 459 460$Revision: 1.1.1.7 $, Last updated $Date: 2002/02/17 21:56:45 $ 461