1262266Sbapt/* 2262266Sbapt * Copyright (c) 2008 The DragonFly Project. All rights reserved. 3262266Sbapt * 4262266Sbapt * This code is derived from software contributed to The DragonFly Project 5262266Sbapt * by Matthias Schmidt <matthias@dragonflybsd.org>, University of Marburg, 6262266Sbapt * Germany. 7262266Sbapt * 8262266Sbapt * Redistribution and use in source and binary forms, with or without 9262266Sbapt * modification, are permitted provided that the following conditions 10262266Sbapt * are met: 11262266Sbapt * 12262266Sbapt * 1. Redistributions of source code must retain the above copyright 13262266Sbapt * notice, this list of conditions and the following disclaimer. 14262266Sbapt * 2. Redistributions in binary form must reproduce the above copyright 15262266Sbapt * notice, this list of conditions and the following disclaimer in 16262266Sbapt * the documentation and/or other materials provided with the 17262266Sbapt * distribution. 18262266Sbapt * 3. Neither the name of The DragonFly Project nor the names of its 19262266Sbapt * contributors may be used to endorse or promote products derived 20262266Sbapt * from this software without specific, prior written permission. 21262266Sbapt * 22262266Sbapt * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23262266Sbapt * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24262266Sbapt * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 25262266Sbapt * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 26262266Sbapt * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 27262266Sbapt * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 28262266Sbapt * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 29262266Sbapt * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 30262266Sbapt * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 31262266Sbapt * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 32262266Sbapt * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33262266Sbapt * SUCH DAMAGE. 34262266Sbapt */ 35262266Sbapt 36262266Sbapt#include <err.h> 37262266Sbapt#include <errno.h> 38262266Sbapt#include <stdio.h> 39262266Sbapt#include <stdlib.h> 40262266Sbapt#include <string.h> 41262266Sbapt#include <syslog.h> 42262266Sbapt#include <stdarg.h> 43262266Sbapt 44262266Sbapt#include "dma.h" 45262266Sbapt 46262266Sbapt#define DP ": \t" 47262266Sbapt#define EQS " \t" 48262266Sbapt 49262266Sbapt 50262266Sbapt/* 51262266Sbapt * Remove trailing \n's 52262266Sbapt */ 53262266Sbaptvoid 54262266Sbapttrim_line(char *line) 55262266Sbapt{ 56262266Sbapt size_t linelen; 57262266Sbapt char *p; 58262266Sbapt 59262266Sbapt if ((p = strchr(line, '\n'))) 60262266Sbapt *p = (char)0; 61262266Sbapt 62262266Sbapt /* Escape leading dot in every case */ 63262266Sbapt linelen = strlen(line); 64262266Sbapt if (line[0] == '.') { 65262266Sbapt if ((linelen + 2) > 1000) { 66262266Sbapt syslog(LOG_CRIT, "Cannot escape leading dot. Buffer overflow"); 67289123Sbapt exit(EX_DATAERR); 68262266Sbapt } 69262266Sbapt memmove((line + 1), line, (linelen + 1)); 70262266Sbapt line[0] = '.'; 71262266Sbapt } 72262266Sbapt} 73262266Sbapt 74262266Sbaptstatic void 75262266Sbaptchomp(char *str) 76262266Sbapt{ 77262266Sbapt size_t len = strlen(str); 78262266Sbapt 79262266Sbapt if (len == 0) 80262266Sbapt return; 81262266Sbapt if (str[len - 1] == '\n') 82262266Sbapt str[len - 1] = 0; 83262266Sbapt} 84262266Sbapt 85262266Sbapt/* 86262266Sbapt * Read the SMTP authentication config file 87262266Sbapt * 88262266Sbapt * file format is: 89262266Sbapt * user|host:password 90262266Sbapt * 91262266Sbapt * A line starting with # is treated as comment and ignored. 92262266Sbapt */ 93262266Sbaptvoid 94262266Sbaptparse_authfile(const char *path) 95262266Sbapt{ 96262266Sbapt char line[2048]; 97262266Sbapt struct authuser *au; 98262266Sbapt FILE *a; 99262266Sbapt char *data; 100262266Sbapt int lineno = 0; 101262266Sbapt 102262266Sbapt a = fopen(path, "r"); 103262266Sbapt if (a == NULL) { 104289123Sbapt errlog(EX_NOINPUT, "can not open auth file `%s'", path); 105262266Sbapt /* NOTREACHED */ 106262266Sbapt } 107262266Sbapt 108262266Sbapt while (!feof(a)) { 109262266Sbapt if (fgets(line, sizeof(line), a) == NULL) 110262266Sbapt break; 111262266Sbapt lineno++; 112262266Sbapt 113262266Sbapt chomp(line); 114262266Sbapt 115262266Sbapt /* We hit a comment */ 116262266Sbapt if (*line == '#') 117262266Sbapt continue; 118262266Sbapt /* Ignore empty lines */ 119262266Sbapt if (*line == 0) 120262266Sbapt continue; 121262266Sbapt 122262266Sbapt au = calloc(1, sizeof(*au)); 123262266Sbapt if (au == NULL) 124289144Sbapt errlog(EX_OSERR, "calloc()"); 125262266Sbapt 126262266Sbapt data = strdup(line); 127262266Sbapt au->login = strsep(&data, "|"); 128262266Sbapt au->host = strsep(&data, DP); 129262266Sbapt au->password = data; 130262266Sbapt 131262266Sbapt if (au->login == NULL || 132262266Sbapt au->host == NULL || 133262266Sbapt au->password == NULL) { 134289123Sbapt errlogx(EX_CONFIG, "syntax error in authfile %s:%d", path, lineno); 135262266Sbapt /* NOTREACHED */ 136262266Sbapt } 137262266Sbapt 138262266Sbapt SLIST_INSERT_HEAD(&authusers, au, next); 139262266Sbapt } 140262266Sbapt 141262266Sbapt fclose(a); 142262266Sbapt} 143262266Sbapt 144262266Sbapt/* 145262266Sbapt * XXX TODO 146262266Sbapt * Check for bad things[TM] 147262266Sbapt */ 148262266Sbaptvoid 149262266Sbaptparse_conf(const char *config_path) 150262266Sbapt{ 151262266Sbapt char *word; 152262266Sbapt char *data; 153262266Sbapt FILE *conf; 154262266Sbapt char line[2048]; 155262266Sbapt int lineno = 0; 156262266Sbapt 157262266Sbapt conf = fopen(config_path, "r"); 158262266Sbapt if (conf == NULL) { 159262266Sbapt /* Don't treat a non-existing config file as error */ 160262266Sbapt if (errno == ENOENT) 161262266Sbapt return; 162289123Sbapt errlog(EX_NOINPUT, "can not open config `%s'", config_path); 163262266Sbapt /* NOTREACHED */ 164262266Sbapt } 165262266Sbapt 166262266Sbapt while (!feof(conf)) { 167262266Sbapt if (fgets(line, sizeof(line), conf) == NULL) 168262266Sbapt break; 169262266Sbapt lineno++; 170262266Sbapt 171262266Sbapt chomp(line); 172262266Sbapt 173262266Sbapt /* We hit a comment */ 174262266Sbapt if (strchr(line, '#')) 175262266Sbapt *strchr(line, '#') = 0; 176262266Sbapt 177262266Sbapt data = line; 178262266Sbapt word = strsep(&data, EQS); 179262266Sbapt 180262266Sbapt /* Ignore empty lines */ 181262266Sbapt if (word == NULL || *word == 0) 182262266Sbapt continue; 183262266Sbapt 184262266Sbapt if (data != NULL && *data != 0) 185262266Sbapt data = strdup(data); 186262266Sbapt else 187262266Sbapt data = NULL; 188262266Sbapt 189262266Sbapt if (strcmp(word, "SMARTHOST") == 0 && data != NULL) 190262266Sbapt config.smarthost = data; 191262266Sbapt else if (strcmp(word, "PORT") == 0 && data != NULL) 192262266Sbapt config.port = atoi(data); 193262266Sbapt else if (strcmp(word, "ALIASES") == 0 && data != NULL) 194262266Sbapt config.aliases = data; 195262266Sbapt else if (strcmp(word, "SPOOLDIR") == 0 && data != NULL) 196262266Sbapt config.spooldir = data; 197262266Sbapt else if (strcmp(word, "AUTHPATH") == 0 && data != NULL) 198262266Sbapt config.authpath= data; 199262266Sbapt else if (strcmp(word, "CERTFILE") == 0 && data != NULL) 200262266Sbapt config.certfile = data; 201262266Sbapt else if (strcmp(word, "MAILNAME") == 0 && data != NULL) 202262266Sbapt config.mailname = data; 203262266Sbapt else if (strcmp(word, "MASQUERADE") == 0 && data != NULL) { 204262266Sbapt char *user = NULL, *host = NULL; 205262266Sbapt if (strrchr(data, '@')) { 206262266Sbapt host = strrchr(data, '@'); 207262266Sbapt *host = 0; 208262266Sbapt host++; 209262266Sbapt user = data; 210262266Sbapt } else { 211262266Sbapt host = data; 212262266Sbapt } 213289123Sbapt if (host && *host == 0) 214262266Sbapt host = NULL; 215262266Sbapt if (user && *user == 0) 216262266Sbapt user = NULL; 217262266Sbapt config.masquerade_host = host; 218262266Sbapt config.masquerade_user = user; 219262266Sbapt } else if (strcmp(word, "STARTTLS") == 0 && data == NULL) 220262266Sbapt config.features |= STARTTLS; 221262266Sbapt else if (strcmp(word, "OPPORTUNISTIC_TLS") == 0 && data == NULL) 222262266Sbapt config.features |= TLS_OPP; 223262266Sbapt else if (strcmp(word, "SECURETRANSFER") == 0 && data == NULL) 224262266Sbapt config.features |= SECURETRANS; 225262266Sbapt else if (strcmp(word, "DEFER") == 0 && data == NULL) 226262266Sbapt config.features |= DEFER; 227262266Sbapt else if (strcmp(word, "INSECURE") == 0 && data == NULL) 228262266Sbapt config.features |= INSECURE; 229262266Sbapt else if (strcmp(word, "FULLBOUNCE") == 0 && data == NULL) 230262266Sbapt config.features |= FULLBOUNCE; 231262266Sbapt else if (strcmp(word, "NULLCLIENT") == 0 && data == NULL) 232262266Sbapt config.features |= NULLCLIENT; 233262266Sbapt else { 234289123Sbapt errlogx(EX_CONFIG, "syntax error in %s:%d", config_path, lineno); 235262266Sbapt /* NOTREACHED */ 236262266Sbapt } 237262266Sbapt } 238262266Sbapt 239262266Sbapt if ((config.features & NULLCLIENT) && config.smarthost == NULL) { 240289123Sbapt errlogx(EX_CONFIG, "%s: NULLCLIENT requires SMARTHOST", config_path); 241262266Sbapt /* NOTREACHED */ 242262266Sbapt } 243262266Sbapt 244262266Sbapt fclose(conf); 245262266Sbapt} 246