]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/sendmail/makemap/makemap.c
Merge sendmail 8.16.1 to HEAD: See contrib/sendmail/RELEASE_NOTES for details
[FreeBSD/FreeBSD.git] / contrib / sendmail / makemap / makemap.c
1 /*
2  * Copyright (c) 1998-2002, 2004, 2008 Proofpoint, Inc. and its suppliers.
3  *      All rights reserved.
4  * Copyright (c) 1992 Eric P. Allman.  All rights reserved.
5  * Copyright (c) 1992, 1993
6  *      The Regents of the University of California.  All rights reserved.
7  *
8  * By using this file, you agree to the terms and conditions set
9  * forth in the LICENSE file which can be found at the top level of
10  * the sendmail distribution.
11  *
12  */
13
14 #include <sm/gen.h>
15
16 SM_IDSTR(copyright,
17 "@(#) Copyright (c) 1998-2002, 2004 Proofpoint, Inc. and its suppliers.\n\
18         All rights reserved.\n\
19      Copyright (c) 1992 Eric P. Allman.  All rights reserved.\n\
20      Copyright (c) 1992, 1993\n\
21         The Regents of the University of California.  All rights reserved.\n")
22
23 SM_IDSTR(id, "@(#)$Id: makemap.c,v 8.183 2013-11-22 20:51:52 ca Exp $")
24
25
26 #include <sys/types.h>
27 #ifndef ISC_UNIX
28 # include <sys/file.h>
29 #endif
30 #include <ctype.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #ifdef EX_OK
34 # undef EX_OK           /* unistd.h may have another use for this */
35 #endif
36 #include <sysexits.h>
37 #include <sendmail/sendmail.h>
38 #include <sm/path.h>
39 #include <sendmail/pathnames.h>
40 #include <libsmdb/smdb.h>
41
42 uid_t   RealUid;
43 gid_t   RealGid;
44 char    *RealUserName;
45 uid_t   RunAsUid;
46 gid_t   RunAsGid;
47 char    *RunAsUserName;
48 int     Verbose = 2;
49 bool    DontInitGroups = false;
50 uid_t   TrustedUid = 0;
51 BITMAP256 DontBlameSendmail;
52
53 #define BUFSIZE         1024
54 #define ISASCII(c)      isascii((unsigned char)(c))
55 #define ISSEP(c) (sep == '\0' ? ISASCII(c) && isspace(c) : (c) == sep)
56
57 static void usage __P((const char *));
58 static char *readcf __P((const char *, char *, bool));
59
60 static void
61 usage(progname)
62         const char *progname;
63 {
64         sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
65                       "Usage: %s [-C cffile] [-N] [-c cachesize] [-D commentchar]\n",
66                       progname);
67         sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
68                       "       %*s [-d] [-e] [-f] [-l] [-o] [-r] [-s] [-t delimiter]\n",
69                       (int) strlen(progname), "");
70         sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
71                       "       %*s [-u] [-v] type mapname\n",
72                       (int) strlen(progname), "");
73         exit(EX_USAGE);
74 }
75
76 /*
77 **  READCF -- read some settings from configuration file.
78 **
79 **      Parameters:
80 **              cfile -- configuration file name.
81 **              mapfile -- file name of map to look up (if not NULL/empty)
82 **                      Note: this finds the first match, so in case someone
83 **                      uses the same map file for different maps, they are
84 **                      hopefully using the same map type.
85 **              fullpath -- compare the full paths or just the "basename"s?
86 **                      (even excluding any .ext !)
87 **
88 **      Returns:
89 **              pointer to map class name (static!)
90 */
91
92 static char *
93 readcf(cfile, mapfile, fullpath)
94         const char *cfile;
95         char *mapfile;
96         bool fullpath;
97 {
98         SM_FILE_T *cfp;
99         char buf[MAXLINE];
100         static char classbuf[MAXLINE];
101         char *classname;
102         char *p;
103
104         if ((cfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, cfile,
105                               SM_IO_RDONLY, NULL)) == NULL)
106         {
107                 sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
108                               "makemap: %s: %s\n",
109                               cfile, sm_errstring(errno));
110                 exit(EX_NOINPUT);
111         }
112         classname = NULL;
113         classbuf[0] = '\0';
114
115         if (!fullpath && mapfile != NULL)
116         {
117                 p = strrchr(mapfile, '/');
118                 if (p != NULL)
119                         mapfile = ++p;
120                 p = strrchr(mapfile, '.');
121                 if (p != NULL)
122                         *p = '\0';
123         }
124
125         while (sm_io_fgets(cfp, SM_TIME_DEFAULT, buf, sizeof(buf)) >= 0)
126         {
127                 char *b;
128
129                 if ((b = strchr(buf, '\n')) != NULL)
130                         *b = '\0';
131
132                 b = buf;
133                 switch (*b++)
134                 {
135                   case 'O':             /* option */
136 #if HASFCHOWN
137                         if (strncasecmp(b, " TrustedUser", 12) == 0 &&
138                             !(ISASCII(b[12]) && isalnum(b[12])))
139                         {
140                                 b = strchr(b, '=');
141                                 if (b == NULL)
142                                         continue;
143                                 while (ISASCII(*++b) && isspace(*b))
144                                         continue;
145                                 if (ISASCII(*b) && isdigit(*b))
146                                         TrustedUid = atoi(b);
147                                 else
148                                 {
149                                         struct passwd *pw;
150
151                                         TrustedUid = 0;
152                                         pw = getpwnam(b);
153                                         if (pw == NULL)
154                                                 (void) sm_io_fprintf(smioerr,
155                                                                      SM_TIME_DEFAULT,
156                                                                      "TrustedUser: unknown user %s\n", b);
157                                         else
158                                                 TrustedUid = pw->pw_uid;
159                                 }
160
161 # ifdef UID_MAX
162                                 if (TrustedUid > UID_MAX)
163                                 {
164                                         (void) sm_io_fprintf(smioerr,
165                                                              SM_TIME_DEFAULT,
166                                                              "TrustedUser: uid value (%ld) > UID_MAX (%ld)",
167                                                 (long) TrustedUid,
168                                                 (long) UID_MAX);
169                                         TrustedUid = 0;
170                                 }
171 # endif /* UID_MAX */
172                         }
173 #endif /* HASFCHOWN */
174                         break;
175
176                   case 'K':             /* Keyfile (map) */
177                         if (classname != NULL)  /* found it already */
178                                 continue;
179                         if (mapfile == NULL || *mapfile == '\0')
180                                 continue;
181
182                         /* cut off trailing spaces */
183                         for (p = buf + strlen(buf) - 1; ISASCII(*p) && isspace(*p) && p > buf; p--)
184                                 *p = '\0';
185
186                         /* find the last argument */
187                         p = strrchr(buf, ' ');
188                         if (p == NULL)
189                                 continue;
190                         b = strstr(p, mapfile);
191                         if (b == NULL)
192                                 continue;
193                         if (b <= buf)
194                                 continue;
195                         if (!fullpath)
196                         {
197                                 p = strrchr(b, '.');
198                                 if (p != NULL)
199                                         *p = '\0';
200                         }
201
202                         /* allow trailing white space? */
203                         if (strcmp(mapfile, b) != 0)
204                                 continue;
205                         /* SM_ASSERT(b > buf); */
206                         --b;
207                         if (!ISASCII(*b))
208                                 continue;
209                         if (!isspace(*b) && fullpath)
210                                 continue;
211                         if (!fullpath && !(SM_IS_DIR_DELIM(*b) || isspace(*b)))
212                                 continue;
213
214                         /* basically from readcf.c */
215                         for (b = buf + 1; ISASCII(*b) && isspace(*b); b++)
216                                 ;
217                         if (!(ISASCII(*b) && isalnum(*b)))
218                         {
219                                 /* syserr("readcf: config K line: no map name"); */
220                                 return NULL;
221                         }
222
223                         while ((ISASCII(*++b) && isalnum(*b)) || *b == '_' || *b == '.')
224                                 ;
225                         if (*b != '\0')
226                                 *b++ = '\0';
227                         while (ISASCII(*b) && isspace(*b))
228                                 b++;
229                         if (!(ISASCII(*b) && isalnum(*b)))
230                         {
231                                 /* syserr("readcf: config K line, map %s: no map class", b); */
232                                 return NULL;
233                         }
234                         classname = b;
235                         while (ISASCII(*++b) && isalnum(*b))
236                                 ;
237                         if (*b != '\0')
238                                 *b++ = '\0';
239                         (void) sm_strlcpy(classbuf, classname, sizeof classbuf);
240                         break;
241
242                   default:
243                         continue;
244                 }
245         }
246         (void) sm_io_close(cfp, SM_TIME_DEFAULT);
247
248         return classbuf;
249 }
250
251 int
252 main(argc, argv)
253         int argc;
254         char **argv;
255 {
256         char *progname;
257         char *cfile;
258         bool inclnull = false;
259         bool notrunc = false;
260         bool allowreplace = false;
261         bool allowempty = false;
262         bool verbose = false;
263         bool foldcase = true;
264         bool unmake = false;
265         bool didreadcf = false;
266         char sep = '\0';
267         char comment = '#';
268         int exitstat;
269         int opt;
270         char *typename = NULL;
271         char *fallback = NULL;
272         char *mapname = NULL;
273         unsigned int lineno;
274         int st;
275         int mode;
276         int smode;
277         int putflags = 0;
278         long sff = SFF_ROOTOK|SFF_REGONLY;
279         struct passwd *pw;
280         SMDB_DATABASE *database;
281         SMDB_CURSOR *cursor;
282         SMDB_DBENT db_key, db_val;
283         SMDB_DBPARAMS params;
284         SMDB_USER_INFO user_info;
285         char ibuf[BUFSIZE];
286         static char rnamebuf[MAXNAME];  /* holds RealUserName */
287         extern char *optarg;
288         extern int optind;
289
290         memset(&params, '\0', sizeof params);
291         params.smdbp_cache_size = 1024 * 1024;
292
293         progname = strrchr(argv[0], '/');
294         if (progname != NULL)
295                 progname++;
296         else
297                 progname = argv[0];
298         cfile = getcfname(0, 0, SM_GET_SENDMAIL_CF, NULL);
299
300         clrbitmap(DontBlameSendmail);
301         RunAsUid = RealUid = getuid();
302         RunAsGid = RealGid = getgid();
303         pw = getpwuid(RealUid);
304         if (pw != NULL)
305                 (void) sm_strlcpy(rnamebuf, pw->pw_name, sizeof rnamebuf);
306         else
307                 (void) sm_snprintf(rnamebuf, sizeof rnamebuf,
308                     "Unknown UID %d", (int) RealUid);
309         RunAsUserName = RealUserName = rnamebuf;
310         user_info.smdbu_id = RunAsUid;
311         user_info.smdbu_group_id = RunAsGid;
312         (void) sm_strlcpy(user_info.smdbu_name, RunAsUserName,
313                        SMDB_MAX_USER_NAME_LEN);
314
315 #define OPTIONS         "C:D:Nc:defi:Llorst:uvx"
316         while ((opt = getopt(argc, argv, OPTIONS)) != -1)
317         {
318                 switch (opt)
319                 {
320                   case 'C':
321                         cfile = optarg;
322                         break;
323
324                   case 'N':
325                         inclnull = true;
326                         break;
327
328                   case 'c':
329                         params.smdbp_cache_size = atol(optarg);
330                         break;
331
332                   case 'd':
333                         params.smdbp_allow_dup = true;
334                         break;
335
336                   case 'e':
337                         allowempty = true;
338                         break;
339
340                   case 'f':
341                         foldcase = false;
342                         break;
343
344                   case 'i':
345                         fallback =optarg;
346                         break;
347
348                   case 'D':
349                         comment = *optarg;
350                         break;
351
352                   case 'L':
353                         smdb_print_available_types(false);
354                         sm_io_fprintf(smioout, SM_TIME_DEFAULT,
355                                       "cf\nCF\n");
356                         exit(EX_OK);
357                         break;
358
359                   case 'l':
360                         smdb_print_available_types(false);
361                         exit(EX_OK);
362                         break;
363
364                   case 'o':
365                         notrunc = true;
366                         break;
367
368                   case 'r':
369                         allowreplace = true;
370                         break;
371
372                   case 's':
373                         setbitn(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail);
374                         setbitn(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail);
375                         setbitn(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail);
376                         setbitn(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail);
377                         break;
378
379                   case 't':
380                         if (optarg == NULL || *optarg == '\0')
381                         {
382                                 sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
383                                               "Invalid separator\n");
384                                 break;
385                         }
386                         sep = *optarg;
387                         break;
388
389                   case 'u':
390                         unmake = true;
391                         break;
392
393                   case 'v':
394                         verbose = true;
395                         break;
396  
397                   case 'x':
398                         smdb_print_available_types(true);
399                         exit(EX_OK);
400                         break;
401
402                   default:
403                         usage(progname);
404                         /* NOTREACHED */
405                 }
406         }
407
408         if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
409                 sff |= SFF_NOSLINK;
410         if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
411                 sff |= SFF_NOHLINK;
412         if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
413                 sff |= SFF_NOWLINK;
414
415         argc -= optind;
416         argv += optind;
417         if (argc != 2)
418         {
419                 usage(progname);
420                 /* NOTREACHED */
421         }
422         else
423         {
424                 typename = argv[0];
425                 mapname = argv[1];
426         }
427
428 #define TYPEFROMCF      (strcasecmp(typename, "cf") == 0)
429 #define FULLPATHFROMCF  (strcmp(typename, "cf") == 0)
430
431 #if HASFCHOWN
432         if (geteuid() == 0)
433         {
434                 if (TYPEFROMCF)
435                         typename = readcf(cfile, mapname, FULLPATHFROMCF);
436                 else
437                         (void) readcf(cfile, NULL, false);
438                 didreadcf = true;
439         }
440 #endif /* HASFCHOWN */
441
442         if (!params.smdbp_allow_dup && !allowreplace)
443                 putflags = SMDBF_NO_OVERWRITE;
444
445         if (unmake)
446         {
447                 mode = O_RDONLY;
448                 smode = S_IRUSR;
449         }
450         else
451         {
452                 mode = O_RDWR;
453                 if (!notrunc)
454                 {
455                         mode |= O_CREAT|O_TRUNC;
456                         sff |= SFF_CREAT;
457                 }
458                 smode = S_IWUSR;
459         }
460
461         params.smdbp_num_elements = 4096;
462
463         if (!didreadcf && TYPEFROMCF)
464         {
465                 typename = readcf(cfile, mapname, FULLPATHFROMCF);
466                 didreadcf = true;
467         }
468         if (didreadcf && (typename == NULL || *typename == '\0'))
469         {
470                 if (fallback != NULL && *fallback != '\0')
471                 {
472                         typename = fallback;
473                         if (verbose)
474                                 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
475                                      "%s: mapfile %s: not found in %s, using fallback %s\n",
476                                      progname, mapname, cfile, fallback);
477                 }
478                 else
479                 {
480                         (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
481                                      "%s: mapfile %s: not found in %s\n",
482                                      progname, mapname, cfile);
483                         exit(EX_DATAERR);
484                 }
485         }
486
487         /*
488         **  Note: if "implicit" is selected it does not work like
489         **  sendmail: it will just use the first available DB type,
490         **  it won't try several (for -u) to find one that "works".
491         */
492
493         errno = smdb_open_database(&database, mapname, mode, smode, sff,
494                                    typename, &user_info, &params);
495         if (errno != SMDBE_OK)
496         {
497                 char *hint;
498
499                 if (errno == SMDBE_UNSUPPORTED_DB_TYPE &&
500                     (hint = smdb_db_definition(typename)) != NULL)
501                         (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
502                                              "%s: Need to recompile with -D%s for %s support\n",
503                                              progname, hint, typename);
504                 else
505                         (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
506                                              "%s: error opening type %s map %s: %s\n",
507                                              progname, typename, mapname,
508                                              sm_errstring(errno));
509                 exit(EX_CANTCREAT);
510         }
511
512         (void) database->smdb_sync(database, 0);
513
514         if (!unmake && geteuid() == 0 && TrustedUid != 0)
515         {
516                 errno = database->smdb_set_owner(database, TrustedUid, -1);
517                 if (errno != SMDBE_OK)
518                 {
519                         (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
520                                              "WARNING: ownership change on %s failed %s",
521                                              mapname, sm_errstring(errno));
522                 }
523         }
524
525         /*
526         **  Copy the data
527         */
528
529         exitstat = EX_OK;
530         if (unmake)
531         {
532                 errno = database->smdb_cursor(database, &cursor, 0);
533                 if (errno != SMDBE_OK)
534                 {
535
536                         (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
537                                              "%s: cannot make cursor for type %s map %s\n",
538                                              progname, typename, mapname);
539                         exit(EX_SOFTWARE);
540                 }
541
542                 memset(&db_key, '\0', sizeof db_key);
543                 memset(&db_val, '\0', sizeof db_val);
544
545                 for (lineno = 0; ; lineno++)
546                 {
547                         errno = cursor->smdbc_get(cursor, &db_key, &db_val,
548                                                   SMDB_CURSOR_GET_NEXT);
549                         if (errno != SMDBE_OK)
550                                 break;
551
552                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
553                                              "%.*s%c%.*s\n",
554                                              (int) db_key.size,
555                                              (char *) db_key.data,
556                                              (sep != '\0') ? sep : '\t',
557                                              (int) db_val.size,
558                                              (char *)db_val.data);
559
560                 }
561                 (void) cursor->smdbc_close(cursor);
562         }
563         else
564         {
565                 lineno = 0;
566                 while (sm_io_fgets(smioin, SM_TIME_DEFAULT, ibuf, sizeof ibuf)
567                        >= 0)
568                 {
569                         register char *p;
570
571                         lineno++;
572
573                         /*
574                         **  Parse the line.
575                         */
576
577                         p = strchr(ibuf, '\n');
578                         if (p != NULL)
579                                 *p = '\0';
580                         else if (!sm_io_eof(smioin))
581                         {
582                                 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
583                                                      "%s: %s: line %u: line too long (%ld bytes max)\n",
584                                                      progname, mapname, lineno,
585                                                      (long) sizeof ibuf);
586                                 exitstat = EX_DATAERR;
587                                 continue;
588                         }
589
590                         if (ibuf[0] == '\0' || ibuf[0] == comment)
591                                 continue;
592                         if (sep == '\0' && ISASCII(ibuf[0]) && isspace(ibuf[0]))
593                         {
594                                 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
595                                                      "%s: %s: line %u: syntax error (leading space)\n",
596                                                      progname, mapname, lineno);
597                                 exitstat = EX_DATAERR;
598                                 continue;
599                         }
600
601                         memset(&db_key, '\0', sizeof db_key);
602                         memset(&db_val, '\0', sizeof db_val);
603                         db_key.data = ibuf;
604
605                         for (p = ibuf; *p != '\0' && !(ISSEP(*p)); p++)
606                         {
607                                 if (foldcase && ISASCII(*p) && isupper(*p))
608                                         *p = tolower(*p);
609                         }
610                         db_key.size = p - ibuf;
611                         if (inclnull)
612                                 db_key.size++;
613
614                         if (*p != '\0')
615                                 *p++ = '\0';
616                         while (*p != '\0' && ISSEP(*p))
617                                 p++;
618                         if (!allowempty && *p == '\0')
619                         {
620                                 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
621                                                      "%s: %s: line %u: no RHS for LHS %s\n",
622                                                      progname, mapname, lineno,
623                                                      (char *) db_key.data);
624                                 exitstat = EX_DATAERR;
625                                 continue;
626                         }
627
628                         db_val.data = p;
629                         db_val.size = strlen(p);
630                         if (inclnull)
631                                 db_val.size++;
632
633                         /*
634                         **  Do the database insert.
635                         */
636
637                         if (verbose)
638                         {
639                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
640                                                      "key=`%s', val=`%s'\n",
641                                                      (char *) db_key.data,
642                                                      (char *) db_val.data);
643                         }
644
645                         errno = database->smdb_put(database, &db_key, &db_val,
646                                                    putflags);
647                         switch (errno)
648                         {
649                           case SMDBE_KEY_EXIST:
650                                 st = 1;
651                                 break;
652
653                           case 0:
654                                 st = 0;
655                                 break;
656
657                           default:
658                                 st = -1;
659                                 break;
660                         }
661
662                         if (st < 0)
663                         {
664                                 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
665                                                      "%s: %s: line %u: key %s: put error: %s\n",
666                                                      progname, mapname, lineno,
667                                                      (char *) db_key.data,
668                                                      sm_errstring(errno));
669                                 exitstat = EX_IOERR;
670                         }
671                         else if (st > 0)
672                         {
673                                 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
674                                                      "%s: %s: line %u: key %s: duplicate key\n",
675                                                      progname, mapname,
676                                                      lineno,
677                                                      (char *) db_key.data);
678                                 exitstat = EX_DATAERR;
679                         }
680                 }
681         }
682
683         /*
684         **  Now close the database.
685         */
686
687         errno = database->smdb_close(database);
688         if (errno != SMDBE_OK)
689         {
690                 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
691                                      "%s: close(%s): %s\n",
692                                      progname, mapname, sm_errstring(errno));
693                 exitstat = EX_IOERR;
694         }
695         smdb_free_database(database);
696
697         exit(exitstat);
698
699         /* NOTREACHED */
700         return exitstat;
701 }