Initial import from FreeBSD RELENG_4:
[dragonfly.git] / contrib / sendmail / makemap / makemap.c
1 /*
2  * Copyright (c) 1998-2001 Sendmail, 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-2001 Sendmail, 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.175 2001/12/28 22:44:01 ca Exp $")
24
25
26 #include <sys/types.h>
27 #ifndef ISC_UNIX
28 # include <sys/file.h>
29 #endif /* ! ISC_UNIX */
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 /* EX_OK */
36 #include <sysexits.h>
37 #include <sendmail/sendmail.h>
38 #include <sendmail/pathnames.h>
39 #include <libsmdb/smdb.h>
40
41 uid_t   RealUid;
42 gid_t   RealGid;
43 char    *RealUserName;
44 uid_t   RunAsUid;
45 uid_t   RunAsGid;
46 char    *RunAsUserName;
47 int     Verbose = 2;
48 bool    DontInitGroups = false;
49 uid_t   TrustedUid = 0;
50 BITMAP256 DontBlameSendmail;
51
52 #define BUFSIZE         1024
53 #define ISSEP(c) (sep == '\0' ? isascii(c) && isspace(c) : (c) == sep)
54
55 static void
56 usage(progname)
57         char *progname;
58 {
59         /* XXX break the usage output into multiple lines? it's too long */
60         sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
61                 "Usage: %s [-C cffile] [-N] [-c cachesize] [-d] [-e] [-f] [-l] [-o] [-r] [-s] [-t delimiter] [-u] [-v] type mapname\n",
62                 progname);
63 #if _FFR_COMMENT_CHAR
64         /* add -D comment-char */
65 #endif /* _FFR_COMMENT_CHAR */
66         exit(EX_USAGE);
67 }
68
69 int
70 main(argc, argv)
71         int argc;
72         char **argv;
73 {
74         char *progname;
75         char *cfile;
76         bool inclnull = false;
77         bool notrunc = false;
78         bool allowreplace = false;
79         bool allowempty = false;
80         bool verbose = false;
81         bool foldcase = true;
82         bool unmake = false;
83         char sep = '\0';
84         char comment = '#';
85         int exitstat;
86         int opt;
87         char *typename = NULL;
88         char *mapname = NULL;
89         unsigned int lineno;
90         int st;
91         int mode;
92         int smode;
93         int putflags = 0;
94         long sff = SFF_ROOTOK|SFF_REGONLY;
95         struct passwd *pw;
96         SMDB_DATABASE *database;
97         SMDB_CURSOR *cursor;
98         SMDB_DBENT db_key, db_val;
99         SMDB_DBPARAMS params;
100         SMDB_USER_INFO user_info;
101         char ibuf[BUFSIZE];
102 #if HASFCHOWN
103         SM_FILE_T *cfp;
104         char buf[MAXLINE];
105 #endif /* HASFCHOWN */
106         static char rnamebuf[MAXNAME];  /* holds RealUserName */
107         extern char *optarg;
108         extern int optind;
109
110         memset(&params, '\0', sizeof params);
111         params.smdbp_cache_size = 1024 * 1024;
112
113         progname = strrchr(argv[0], '/');
114         if (progname != NULL)
115                 progname++;
116         else
117                 progname = argv[0];
118         cfile = getcfname(0, 0, SM_GET_SENDMAIL_CF, NULL);
119
120         clrbitmap(DontBlameSendmail);
121         RunAsUid = RealUid = getuid();
122         RunAsGid = RealGid = getgid();
123         pw = getpwuid(RealUid);
124         if (pw != NULL)
125                 (void) sm_strlcpy(rnamebuf, pw->pw_name, sizeof rnamebuf);
126         else
127                 (void) sm_snprintf(rnamebuf, sizeof rnamebuf,
128                     "Unknown UID %d", (int) RealUid);
129         RunAsUserName = RealUserName = rnamebuf;
130         user_info.smdbu_id = RunAsUid;
131         user_info.smdbu_group_id = RunAsGid;
132         (void) sm_strlcpy(user_info.smdbu_name, RunAsUserName,
133                        SMDB_MAX_USER_NAME_LEN);
134
135 #define OPTIONS         "C:D:Nc:deflorst:uv"
136         while ((opt = getopt(argc, argv, OPTIONS)) != -1)
137         {
138                 switch (opt)
139                 {
140                   case 'C':
141                         cfile = optarg;
142                         break;
143
144                   case 'N':
145                         inclnull = true;
146                         break;
147
148                   case 'c':
149                         params.smdbp_cache_size = atol(optarg);
150                         break;
151
152                   case 'd':
153                         params.smdbp_allow_dup = true;
154                         break;
155
156                   case 'e':
157                         allowempty = true;
158                         break;
159
160                   case 'f':
161                         foldcase = false;
162                         break;
163
164 #if _FFR_COMMENT_CHAR
165                   case 'D':
166                         comment = *optarg;
167                         break;
168 #endif /* _FFR_COMMENT_CHAR */
169
170                   case 'l':
171                         smdb_print_available_types();
172                         exit(EX_OK);
173                         break;
174
175                   case 'o':
176                         notrunc = true;
177                         break;
178
179                   case 'r':
180                         allowreplace = true;
181                         break;
182
183                   case 's':
184                         setbitn(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail);
185                         setbitn(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail);
186                         setbitn(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail);
187                         setbitn(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail);
188                         break;
189
190                   case 't':
191                         if (optarg == NULL || *optarg == '\0')
192                         {
193                                 sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
194                                               "Invalid separator\n");
195                                 break;
196                         }
197                         sep = *optarg;
198                         break;
199
200                   case 'u':
201                         unmake = true;
202                         break;
203
204                   case 'v':
205                         verbose = true;
206                         break;
207
208                   default:
209                         usage(progname);
210                         /* NOTREACHED */
211                 }
212         }
213
214         if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
215                 sff |= SFF_NOSLINK;
216         if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
217                 sff |= SFF_NOHLINK;
218         if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
219                 sff |= SFF_NOWLINK;
220
221         argc -= optind;
222         argv += optind;
223         if (argc != 2)
224         {
225                 usage(progname);
226                 /* NOTREACHED */
227         }
228         else
229         {
230                 typename = argv[0];
231                 mapname = argv[1];
232         }
233
234 #if HASFCHOWN
235         /* Find TrustedUser value in sendmail.cf */
236         if ((cfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, cfile, SM_IO_RDONLY,
237                               NULL)) == NULL)
238         {
239                 sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "makemap: %s: %s",
240                               cfile, sm_errstring(errno));
241                 exit(EX_NOINPUT);
242         }
243         while (sm_io_fgets(cfp, SM_TIME_DEFAULT, buf, sizeof(buf)) != NULL)
244         {
245                 register char *b;
246
247                 if ((b = strchr(buf, '\n')) != NULL)
248                         *b = '\0';
249
250                 b = buf;
251                 switch (*b++)
252                 {
253                   case 'O':             /* option */
254                         if (strncasecmp(b, " TrustedUser", 12) == 0 &&
255                             !(isascii(b[12]) && isalnum(b[12])))
256                         {
257                                 b = strchr(b, '=');
258                                 if (b == NULL)
259                                         continue;
260                                 while (isascii(*++b) && isspace(*b))
261                                         continue;
262                                 if (isascii(*b) && isdigit(*b))
263                                         TrustedUid = atoi(b);
264                                 else
265                                 {
266                                         TrustedUid = 0;
267                                         pw = getpwnam(b);
268                                         if (pw == NULL)
269                                                 (void) sm_io_fprintf(smioerr,
270                                                                      SM_TIME_DEFAULT,
271                                                                      "TrustedUser: unknown user %s\n", b);
272                                         else
273                                                 TrustedUid = pw->pw_uid;
274                                 }
275
276 # ifdef UID_MAX
277                                 if (TrustedUid > UID_MAX)
278                                 {
279                                         (void) sm_io_fprintf(smioerr,
280                                                              SM_TIME_DEFAULT,
281                                                              "TrustedUser: uid value (%ld) > UID_MAX (%ld)",
282                                                 (long) TrustedUid,
283                                                 (long) UID_MAX);
284                                         TrustedUid = 0;
285                                 }
286 # endif /* UID_MAX */
287                                 break;
288                         }
289
290
291                   default:
292                         continue;
293                 }
294         }
295         (void) sm_io_close(cfp, SM_TIME_DEFAULT);
296 #endif /* HASFCHOWN */
297
298         if (!params.smdbp_allow_dup && !allowreplace)
299                 putflags = SMDBF_NO_OVERWRITE;
300
301         if (unmake)
302         {
303                 mode = O_RDONLY;
304                 smode = S_IRUSR;
305         }
306         else
307         {
308                 mode = O_RDWR;
309                 if (!notrunc)
310                 {
311                         mode |= O_CREAT|O_TRUNC;
312                         sff |= SFF_CREAT;
313                 }
314                 smode = S_IWUSR;
315         }
316
317         params.smdbp_num_elements = 4096;
318
319         errno = smdb_open_database(&database, mapname, mode, smode, sff,
320                                    typename, &user_info, &params);
321         if (errno != SMDBE_OK)
322         {
323                 char *hint;
324
325                 if (errno == SMDBE_UNSUPPORTED_DB_TYPE &&
326                     (hint = smdb_db_definition(typename)) != NULL)
327                         (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
328                                              "%s: Need to recompile with -D%s for %s support\n",
329                                              progname, hint, typename);
330                 else
331                         (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
332                                              "%s: error opening type %s map %s: %s\n",
333                                              progname, typename, mapname,
334                                              sm_errstring(errno));
335                 exit(EX_CANTCREAT);
336         }
337
338         (void) database->smdb_sync(database, 0);
339
340         if (!unmake && geteuid() == 0 && TrustedUid != 0)
341         {
342                 errno = database->smdb_set_owner(database, TrustedUid, -1);
343                 if (errno != SMDBE_OK)
344                 {
345                         (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
346                                              "WARNING: ownership change on %s failed %s",
347                                              mapname, sm_errstring(errno));
348                 }
349         }
350
351         /*
352         **  Copy the data
353         */
354
355         exitstat = EX_OK;
356         if (unmake)
357         {
358                 errno = database->smdb_cursor(database, &cursor, 0);
359                 if (errno != SMDBE_OK)
360                 {
361
362                         (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
363                                              "%s: cannot make cursor for type %s map %s\n",
364                                              progname, typename, mapname);
365                         exit(EX_SOFTWARE);
366                 }
367
368                 memset(&db_key, '\0', sizeof db_key);
369                 memset(&db_val, '\0', sizeof db_val);
370
371                 for (lineno = 0; ; lineno++)
372                 {
373                         errno = cursor->smdbc_get(cursor, &db_key, &db_val,
374                                                   SMDB_CURSOR_GET_NEXT);
375                         if (errno != SMDBE_OK)
376                                 break;
377
378                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
379                                              "%.*s\t%.*s\n",
380                                              (int) db_key.size,
381                                              (char *) db_key.data,
382                                              (int) db_val.size,
383                                              (char *)db_val.data);
384
385                 }
386                 (void) cursor->smdbc_close(cursor);
387         }
388         else
389         {
390                 lineno = 0;
391                 while (sm_io_fgets(smioin, SM_TIME_DEFAULT, ibuf, sizeof ibuf)
392                        != NULL)
393                 {
394                         register char *p;
395
396                         lineno++;
397
398                         /*
399                         **  Parse the line.
400                         */
401
402                         p = strchr(ibuf, '\n');
403                         if (p != NULL)
404                                 *p = '\0';
405                         else if (!sm_io_eof(smioin))
406                         {
407                                 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
408                                                      "%s: %s: line %u: line too long (%ld bytes max)\n",
409                                                      progname, mapname, lineno,
410                                                      (long) sizeof ibuf);
411                                 exitstat = EX_DATAERR;
412                                 continue;
413                         }
414
415                         if (ibuf[0] == '\0' || ibuf[0] == comment)
416                                 continue;
417                         if (sep == '\0' && isascii(ibuf[0]) && isspace(ibuf[0]))
418                         {
419                                 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
420                                                      "%s: %s: line %u: syntax error (leading space)\n",
421                                                      progname, mapname, lineno);
422                                 exitstat = EX_DATAERR;
423                                 continue;
424                         }
425
426                         memset(&db_key, '\0', sizeof db_key);
427                         memset(&db_val, '\0', sizeof db_val);
428                         db_key.data = ibuf;
429
430                         for (p = ibuf; *p != '\0' && !(ISSEP(*p)); p++)
431                         {
432                                 if (foldcase && isascii(*p) && isupper(*p))
433                                         *p = tolower(*p);
434                         }
435                         db_key.size = p - ibuf;
436                         if (inclnull)
437                                 db_key.size++;
438
439                         if (*p != '\0')
440                                 *p++ = '\0';
441                         while (*p != '\0' && ISSEP(*p))
442                                 p++;
443                         if (!allowempty && *p == '\0')
444                         {
445                                 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
446                                                      "%s: %s: line %u: no RHS for LHS %s\n",
447                                                      progname, mapname, lineno,
448                                                      (char *) db_key.data);
449                                 exitstat = EX_DATAERR;
450                                 continue;
451                         }
452
453                         db_val.data = p;
454                         db_val.size = strlen(p);
455                         if (inclnull)
456                                 db_val.size++;
457
458                         /*
459                         **  Do the database insert.
460                         */
461
462                         if (verbose)
463                         {
464                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
465                                                      "key=`%s', val=`%s'\n",
466                                                      (char *) db_key.data,
467                                                      (char *) db_val.data);
468                         }
469
470                         errno = database->smdb_put(database, &db_key, &db_val,
471                                                    putflags);
472                         switch (errno)
473                         {
474                           case SMDBE_KEY_EXIST:
475                                 st = 1;
476                                 break;
477
478                           case 0:
479                                 st = 0;
480                                 break;
481
482                           default:
483                                 st = -1;
484                                 break;
485                         }
486
487                         if (st < 0)
488                         {
489                                 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
490                                                      "%s: %s: line %u: key %s: put error: %s\n",
491                                                      progname, mapname, lineno,
492                                                      (char *) db_key.data,
493                                                      sm_errstring(errno));
494                                 exitstat = EX_IOERR;
495                         }
496                         else if (st > 0)
497                         {
498                                 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
499                                                      "%s: %s: line %u: key %s: duplicate key\n",
500                                                      progname, mapname,
501                                                      lineno,
502                                                      (char *) db_key.data);
503                                 exitstat = EX_DATAERR;
504                         }
505                 }
506         }
507
508         /*
509         **  Now close the database.
510         */
511
512         errno = database->smdb_close(database);
513         if (errno != SMDBE_OK)
514         {
515                 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
516                                      "%s: close(%s): %s\n",
517                                      progname, mapname, sm_errstring(errno));
518                 exitstat = EX_IOERR;
519         }
520         smdb_free_database(database);
521
522         exit(exitstat);
523
524         /* NOTREACHED */
525         return exitstat;
526 }