Line data Source code
1 6 : int config_bind(CONFIGURATION* config, char* directive, char* params){
2 6 : char* tokenize_line = NULL;
3 6 : char* tokenize_argument = NULL;
4 6 : char* token = NULL;
5 :
6 6 : char* bindhost = NULL;
7 6 : char* port = NULL;
8 :
9 : #ifndef CMAIL_NO_TLS
10 6 : TLSMODE tls_mode=TLS_NONE;
11 6 : char* tls_keyfile = NULL;
12 6 : char* tls_certfile = NULL;
13 6 : char* tls_priorities = NULL;
14 6 : char* tls_dh_paramfile = NULL;
15 : #endif
16 :
17 6 : int listen_fd = -1;
18 6 : int listener_slot = -1;
19 6 : LISTENER settings = {
20 : .fixed_user = NULL,
21 : .max_size = 0,
22 : .announce_domain = "cmail-smtpd",
23 : .auth_offer = AUTH_NONE,
24 : .auth_require = false,
25 : .suppress_submitter = false
26 : };
27 6 : LISTENER* listener_data = NULL;
28 :
29 : //tokenize line
30 6 : bindhost = strtok_r(params, " ", &tokenize_line);
31 : do{
32 52 : token = strtok_r(NULL, " ", &tokenize_line);
33 52 : if(token){
34 46 : if(!port){
35 6 : port = token;
36 : }
37 : #ifndef CMAIL_NO_TLS
38 40 : else if(!strncmp(token, "cert=", 5)){
39 6 : tls_certfile = token + 5;
40 : }
41 34 : else if(!strncmp(token, "key=", 4)){
42 6 : tls_keyfile = token + 4;
43 : }
44 28 : else if(!strcmp(token, "tlsonly")){
45 2 : tls_mode = TLS_ONLY;
46 : }
47 26 : else if(!strncmp(token, "ciphers=", 8)){
48 6 : tls_priorities = token + 8;
49 : }
50 20 : else if(!strncmp(token, "dhparams=", 9)){
51 6 : tls_dh_paramfile = token + 9;
52 : }
53 : #endif
54 14 : else if(!strncmp(token, "auth", 4)){
55 2 : settings.auth_offer = AUTH_ANY;
56 2 : if(token[4] == '='){
57 2 : token = strtok_r(token + 5, ",", &tokenize_argument);
58 8 : while(token){
59 4 : if(!strcmp(token, "tlsonly")){
60 2 : settings.auth_offer = AUTH_TLSONLY;
61 : }
62 2 : else if(!strcmp(token, "strict")){
63 2 : settings.auth_require = true;
64 : }
65 0 : else if(!strcmp(token, "private")){
66 0 : settings.suppress_submitter = true;
67 : }
68 0 : else if(!strncmp(token, "fixed@", 6)){
69 0 : settings.auth_require = true;
70 0 : settings.fixed_user = token + 6;
71 : }
72 : else{
73 0 : logprintf(config->log, LOG_WARNING, "Unknown auth parameter %s\n", token);
74 : }
75 4 : token = strtok_r(NULL, ",", &tokenize_argument);
76 : }
77 :
78 2 : token = ""; //reset to anything but NULL to meet condition
79 : }
80 : }
81 12 : else if(!strncmp(token, "announce=", 9)){
82 6 : settings.announce_domain = token + 9;
83 : }
84 6 : else if(!strncmp(token, "size=", 5)){
85 6 : settings.max_size = strtoul(token + 5, NULL, 10);
86 : }
87 : else{
88 0 : logprintf(config->log, LOG_INFO, "Ignored additional bind parameter %s\n", token);
89 : }
90 : }
91 52 : }while(token);
92 :
93 : #ifndef CMAIL_NO_TLS
94 6 : if(tls_keyfile && tls_certfile){
95 6 : if(tls_mode == TLS_NONE){
96 4 : tls_mode = TLS_NEGOTIATE;
97 : }
98 :
99 12 : if(tls_init_listener(config->log, &settings, tls_certfile, tls_keyfile, tls_dh_paramfile, tls_priorities) < 0){
100 0 : return -1;
101 : }
102 : }
103 0 : else if(tls_keyfile || tls_certfile || tls_mode != TLS_NONE){
104 0 : logprintf(config->log, LOG_ERROR, "Need both certificate and key for TLS\n");
105 0 : return -1;
106 : }
107 : #endif
108 :
109 : //try to open a listening socket
110 6 : listen_fd = network_listener(config->log, bindhost, port);
111 :
112 6 : if(listen_fd < 0){
113 0 : return -1;
114 : }
115 :
116 : //add the new listener to the pool
117 6 : listener_slot = connpool_add(&(config->listeners), listen_fd);
118 6 : if(listener_slot >= 0){
119 6 : logprintf(config->log, LOG_INFO, "Bound to %s port %s (slot %d)\n", bindhost, port, listener_slot);
120 :
121 : //create listener auxdata
122 6 : config->listeners.conns[listener_slot].aux_data = calloc(1, sizeof(LISTENER));
123 6 : if(!config->listeners.conns[listener_slot].aux_data){
124 0 : logprintf(config->log, LOG_ERROR, "Failed to allocate auxiliary data for listener\n");
125 0 : return -1;
126 : }
127 :
128 6 : listener_data = (LISTENER*)config->listeners.conns[listener_slot].aux_data;
129 6 : *listener_data = settings;
130 :
131 : //copy data to heap
132 : #ifndef CMAIL_NO_TLS
133 6 : config->listeners.conns[listener_slot].tls_mode = tls_mode;
134 : #endif
135 :
136 6 : listener_data->announce_domain = common_strdup(settings.announce_domain);
137 6 : if(!listener_data->announce_domain){
138 0 : logprintf(config->log, LOG_ERROR, "Failed to allocate auxiliary data for listener announce\n");
139 0 : return -1;
140 : }
141 :
142 6 : if(settings.fixed_user){
143 0 : listener_data->fixed_user = common_strdup(settings.fixed_user);
144 0 : if(!listener_data->fixed_user){
145 0 : logprintf(config->log, LOG_ERROR, "Failed to allocate memory for fixed user storage\n");
146 0 : return -1;
147 : }
148 : }
149 6 : return 0;
150 : }
151 :
152 0 : logprintf(config->log, LOG_ERROR, "Failed to store listen socket\n");
153 0 : return -1;
154 : }
155 :
156 2 : int config_privileges(CONFIGURATION* config, char* directive, char* params){
157 : struct passwd* user_info;
158 : struct group* group_info;
159 :
160 2 : errno = 0;
161 2 : if(!strcmp(directive, "user")){
162 1 : user_info = getpwnam(params);
163 1 : if(!user_info){
164 0 : logprintf(config->log, LOG_ERROR, "Failed to get user info for %s\n", params);
165 0 : return -1;
166 : }
167 1 : config->privileges.uid = user_info->pw_uid;
168 1 : config->privileges.gid = user_info->pw_gid;
169 1 : logprintf(config->log, LOG_DEBUG, "Configured dropped privileges to uid %d gid %d\n", config->privileges.uid, config->privileges.gid);
170 1 : return 0;
171 : }
172 1 : else if(!strcmp(directive, "group")){
173 1 : group_info = getgrnam(params);
174 1 : if(!group_info){
175 0 : logprintf(config->log, LOG_ERROR, "Failed to get group info for %s\n", params);
176 0 : return -1;
177 : }
178 1 : config->privileges.gid = group_info->gr_gid;
179 1 : logprintf(config->log, LOG_DEBUG, "Configured dropped privileges to gid %d\n", config->privileges.gid);
180 1 : return 0;
181 : }
182 0 : return -1;
183 : }
184 :
185 1 : int config_database(CONFIGURATION* config, char* directive, char* params){
186 1 : if(config->database.conn){
187 0 : logprintf(config->log, LOG_ERROR, "Can not use %s as master database, another one is already attached\n", params);
188 0 : return -1;
189 : }
190 :
191 1 : config->database.conn = database_open(config->log, params, SQLITE_OPEN_READWRITE);
192 :
193 1 : return (config->database.conn) ? 0:-1;
194 : }
195 :
196 2 : int config_logger(CONFIGURATION* config, char* directive, char* params){
197 : FILE* log_file;
198 :
199 2 : if(!strcmp(directive, "verbosity")){
200 1 : config->log.verbosity = strtoul(params, NULL, 10);
201 1 : return 0;
202 : }
203 1 : else if(!strcmp(directive, "logfile")){
204 1 : log_file = fopen(params, "a");
205 1 : if(!log_file){
206 0 : logprintf(config->log, LOG_ERROR, "Failed to open logfile %s for appending\n", params);
207 0 : return -1;
208 : }
209 1 : config->log.stream = log_file;
210 1 : config->log.log_secondary = true;
211 1 : return 0;
212 : }
213 0 : return -1;
214 : }
215 :
216 1 : int config_pidfile(CONFIGURATION* config, char* directive, char* params){
217 1 : if(config->pid_file){
218 0 : logprintf(config->log, LOG_ERROR, "Multiple pidfile stanzas read, aborting\n");
219 0 : return -1;
220 : }
221 :
222 1 : config->pid_file = common_strdup(params);
223 :
224 1 : if(!config->pid_file){
225 0 : logprintf(config->log, LOG_ERROR, "Failed to allocate memory for pidfile path\n");
226 0 : return -1;
227 : }
228 1 : return 0;
229 : }
230 :
231 12 : int config_line(void* config_data, char* line){
232 : unsigned parameter;
233 12 : CONFIGURATION* config = (CONFIGURATION*)config_data;
234 :
235 : //scan over directive
236 12 : for(parameter = 0; (!isspace(line[parameter])) && line[parameter] != 0; parameter++){
237 : }
238 :
239 12 : if(line[parameter] != 0){
240 12 : line[parameter] = 0;
241 12 : parameter++;
242 : }
243 :
244 : //scan for parameter begin
245 12 : for(; isspace(line[parameter]); parameter++){
246 : }
247 :
248 : //route directives
249 12 : if(!strncmp(line, "bind", 4)){
250 6 : return config_bind(config, line, line + parameter);
251 : }
252 :
253 6 : else if(!strncmp(line, "user", 4) || !strncmp(line, "group", 5)){
254 2 : return config_privileges(config, line, line + parameter);
255 : }
256 :
257 4 : else if(!strncmp(line, "database", 8)){
258 1 : return config_database(config, line, line + parameter);
259 : }
260 :
261 3 : else if(!strncmp(line, "verbosity", 9) || !strncmp(line, "logfile", 7)){
262 2 : return config_logger(config, line, line + parameter);
263 : }
264 :
265 1 : else if(!strncmp(line, "pidfile", 7)){
266 1 : return config_pidfile(config, line, line + parameter);
267 : }
268 :
269 0 : logprintf(config->log, LOG_ERROR, "Unknown configuration directive %s\n", line);
270 0 : return -1;
271 : }
272 :
273 2 : void config_free(CONFIGURATION* config){
274 : unsigned i;
275 : LISTENER* listener_data;
276 :
277 14 : for(i = 0; i < config->listeners.count; i++){
278 12 : listener_data = (LISTENER*)config->listeners.conns[i].aux_data;
279 :
280 12 : close(config->listeners.conns[i].fd);
281 :
282 12 : free(listener_data->announce_domain);
283 :
284 12 : if(listener_data->fixed_user){
285 0 : free(listener_data->fixed_user);
286 : }
287 :
288 : #ifndef CMAIL_NO_TLS
289 12 : if(config->listeners.conns[i].tls_mode != TLS_NONE){
290 12 : gnutls_certificate_free_credentials(listener_data->tls_cert);
291 12 : gnutls_priority_deinit(listener_data->tls_priorities);
292 12 : gnutls_dh_params_deinit(listener_data->tls_dhparams);
293 : }
294 : #endif
295 : }
296 :
297 2 : connpool_free(&(config->listeners));
298 2 : database_free(config->log, &(config->database));
299 :
300 2 : if(config->pid_file){
301 2 : free(config->pid_file);
302 : }
303 :
304 2 : if(config->log.stream != stderr){
305 2 : fflush(config->log.stream);
306 2 : fclose(config->log.stream);
307 2 : config->log.stream = stderr;
308 : }
309 2 : }
|