masqmail
diff src/local.c @ 0:08114f7dcc23
this is masqmail-0.2.21 from oliver kurth
author | meillo@marmaro.de |
---|---|
date | Fri, 26 Sep 2008 17:05:23 +0200 |
parents | |
children | 26e34ae9a3e3 |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/local.c Fri Sep 26 17:05:23 2008 +0200 1.3 @@ -0,0 +1,366 @@ 1.4 +/* MasqMail 1.5 + Copyright (C) 1999-2001 Oliver Kurth 1.6 + 1.7 + This program is free software; you can redistribute it and/or modify 1.8 + it under the terms of the GNU General Public License as published by 1.9 + the Free Software Foundation; either version 2 of the License, or 1.10 + (at your option) any later version. 1.11 + 1.12 + This program is distributed in the hope that it will be useful, 1.13 + but WITHOUT ANY WARRANTY; without even the implied warranty of 1.14 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1.15 + GNU General Public License for more details. 1.16 + 1.17 + You should have received a copy of the GNU General Public License 1.18 + along with this program; if not, write to the Free Software 1.19 + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 1.20 +*/ 1.21 + 1.22 +#include "masqmail.h" 1.23 +#include "peopen.h" 1.24 +#include <sys/wait.h> 1.25 + 1.26 +static 1.27 +void message_stream(FILE *out, message *msg, GList *hdr_list, guint flags) 1.28 +{ 1.29 + time_t now = time(NULL); 1.30 + GList *node; 1.31 + 1.32 + if(flags & MSGSTR_FROMLINE){ 1.33 + fprintf(out, "From <%s@%s> %s", msg->return_path->local_part, 1.34 + msg->return_path->domain, ctime(&now)); 1.35 + } 1.36 + 1.37 + foreach(hdr_list, node){ 1.38 + header *hdr = (header *)(node->data); 1.39 + fputs(hdr->header, out); 1.40 + } 1.41 + putc('\n', out); 1.42 + foreach(msg->data_list, node){ 1.43 + /* From hack: */ 1.44 + if(flags & MSGSTR_FROMHACK){ 1.45 + if(strncmp(node->data, "From ", 5) == 0) 1.46 + putc('>', out); 1.47 + } 1.48 + fputs(node->data, out); 1.49 + } 1.50 + putc('\n', out); 1.51 +} 1.52 + 1.53 +gboolean append_file(message *msg, GList *hdr_list, gchar *user) 1.54 +{ 1.55 + struct passwd *pw; 1.56 + gboolean ok = FALSE; 1.57 + 1.58 + /* headers may be special for a local delivery */ 1.59 + if(hdr_list == NULL) 1.60 + hdr_list = msg->hdr_list; 1.61 + 1.62 + if((pw = getpwnam(user))){ 1.63 + uid_t saved_uid = geteuid(); 1.64 + gid_t saved_gid = getegid(); 1.65 + gboolean uid_ok = TRUE, gid_ok = TRUE; 1.66 + 1.67 + if(!conf.run_as_user){ 1.68 + uid_ok = (seteuid(0) == 0); 1.69 + if(uid_ok){ 1.70 + gid_ok = (setegid(conf.mail_gid) == 0); 1.71 + uid_ok = (seteuid(pw->pw_uid) == 0); 1.72 + } 1.73 + } 1.74 + 1.75 + DEBUG(5) debugf("running as euid %d\n", geteuid()); 1.76 + DEBUG(5) debugf("running as egid %d\n", getegid()); 1.77 + 1.78 + if(uid_ok && gid_ok){ 1.79 + gchar *filename; 1.80 + FILE *out; 1.81 + 1.82 + filename = g_strdup_printf("%s/%s", conf.mail_dir, user); 1.83 + if((out = fopen(filename, "a"))){ 1.84 +#ifdef USE_LIBLOCKFILE 1.85 + gint err; 1.86 + /* lock file using liblockfile */ 1.87 + err = maillock(user,3); 1.88 + if(err == 0){ 1.89 +#else 1.90 + /* lock file: */ 1.91 + struct flock lock; 1.92 + lock.l_type = F_WRLCK; 1.93 + lock.l_whence = SEEK_END; 1.94 + lock.l_start = lock.l_len = 0; 1.95 + if(fcntl(fileno(out), F_SETLK, &lock) != -1){ 1.96 +#endif 1.97 + fchmod(fileno(out), 0600); 1.98 + 1.99 + message_stream(out, msg, hdr_list, MSGSTR_FROMLINE|MSGSTR_FROMHACK); 1.100 + 1.101 + ok = TRUE; 1.102 + 1.103 + /* close when still user */ 1.104 + fclose(out); 1.105 +#ifdef USE_LIBLOCKFILE 1.106 + mailunlock(); 1.107 +#endif 1.108 + }else{ 1.109 + fclose(out); 1.110 +#ifdef USE_LIBLOCKFILE 1.111 + DEBUG(3) debugf("could not lock file %s: error %d\n", 1.112 + filename, err); 1.113 + } /* XEmacs indenting convenience... */ 1.114 +#else 1.115 + DEBUG(3) debugf("could not lock file %s: %s\n", 1.116 + filename, strerror(errno)); 1.117 + } 1.118 +#endif 1.119 + }else{ 1.120 + logwrite(LOG_ALERT, "could not open file %s: %s\n", 1.121 + filename, strerror(errno)); 1.122 + } 1.123 + g_free(filename); 1.124 + 1.125 + if(!conf.run_as_user){ 1.126 + uid_ok = (seteuid(0) == 0); 1.127 + if(uid_ok){ 1.128 + gid_ok = (setegid(saved_gid) == 0); 1.129 + uid_ok = (seteuid(saved_uid) == 0); 1.130 + } 1.131 + } 1.132 + 1.133 + if(!uid_ok || !gid_ok){ 1.134 + /* FIXME: if this fails we HAVE to exit, because we shall not run 1.135 + with some users id. But we do not return, and so this message 1.136 + will not be finished, so the user will get the message again 1.137 + next time a delivery is attempted... */ 1.138 + logwrite(LOG_ALERT, 1.139 + "could not set back uid or gid after local delivery: %s\n", 1.140 + strerror(errno)); 1.141 + logwrite(LOG_ALERT, 1.142 + "uid=%d, gid=%d, euid=%d, egid=%d, want = %d, %d\n", 1.143 + getuid(), getgid(), geteuid(), getegid(), saved_uid, saved_gid); 1.144 + exit(EXIT_FAILURE); 1.145 + } 1.146 + }else{ 1.147 + logwrite(LOG_ALERT, 1.148 + "could not set uid or gid for local delivery, uid = %d: %s\n", 1.149 + pw->pw_uid, strerror(errno)); 1.150 + } 1.151 + }else{ 1.152 + logwrite(LOG_ALERT, "could not find password entry for user %s\n", user); 1.153 + errno = ENOENT; /* getpwnam does not set errno correctly */ 1.154 + } 1.155 + 1.156 + return ok; 1.157 +} 1.158 + 1.159 +#ifdef ENABLE_MAILDIR 1.160 +gboolean maildir_out(message *msg, GList *hdr_list, gchar *user, guint flags) 1.161 +{ 1.162 + struct passwd *pw; 1.163 + gboolean ok = FALSE; 1.164 + 1.165 + /* headers may be special for a local delivery */ 1.166 + if(hdr_list == NULL) 1.167 + hdr_list = msg->hdr_list; 1.168 + 1.169 + if((pw = getpwnam(user))){ 1.170 + uid_t saved_uid = geteuid(); 1.171 + gid_t saved_gid = getegid(); 1.172 + gboolean uid_ok = TRUE, gid_ok = TRUE; 1.173 + 1.174 + if(!conf.run_as_user){ 1.175 + uid_ok = (seteuid(0) == 0); 1.176 + if(uid_ok){ 1.177 + gid_ok = (setegid(conf.mail_gid) == 0); 1.178 + uid_ok = (seteuid(pw->pw_uid) == 0); 1.179 + } 1.180 + } 1.181 + 1.182 + DEBUG(5) debugf("running as euid %d\n", geteuid()); 1.183 + DEBUG(5) debugf("running as egid %d\n", getegid()); 1.184 + 1.185 + if(uid_ok && gid_ok){ 1.186 + char *path = g_strdup_printf("%s/Maildir", pw->pw_dir); 1.187 + struct stat statbuf; 1.188 + int ret; 1.189 + 1.190 + DEBUG(5) debugf("path = %s\n", path); 1.191 + 1.192 + ok = TRUE; 1.193 + ret = stat(path, &statbuf); 1.194 + if(ret != 0){ 1.195 + ok = FALSE; 1.196 + if(errno == ENOENT){ 1.197 + logwrite(LOG_NOTICE, "directory %s does not exist, creating\n", path); 1.198 + if(mkdir(path, 0700) == 0) 1.199 + ok = TRUE; 1.200 + }else 1.201 + logwrite(LOG_ALERT, "stat of %s failed: %s\n", path, strerror(errno)); 1.202 + } 1.203 + if(ok){ 1.204 + ok = FALSE; 1.205 + ret = stat(path, &statbuf); 1.206 + if(S_ISDIR(statbuf.st_mode)){ 1.207 + gchar *subdirs[] = {"tmp", "new", "cur"}; 1.208 + int i; 1.209 + for(i = 0; i < 3; i++){ 1.210 + char *path1 = g_strdup_printf("%s/%s", path, subdirs[i]); 1.211 + ret = stat(path1, &statbuf); 1.212 + if(ret != 0){ 1.213 + if(errno == ENOENT){ 1.214 + logwrite(LOG_NOTICE, "directory %s does not exist, creating\n", path1); 1.215 + if(mkdir(path1, 0700) != 0) break; 1.216 + } 1.217 + } 1.218 + g_free(path1); 1.219 + } 1.220 + if(i == 3){ 1.221 + FILE *out; 1.222 + mode_t saved_mode = umask(066); 1.223 + /* the qmail style unique works only if delivering 1.224 + with different process. We do not fork for each delivery, 1.225 + so our uid is more unique. Hope it is compatible with all 1.226 + MUAs. 1.227 + */ 1.228 + gchar *filename = g_strdup_printf("%s/tmp/%s.%s", path, msg->uid, conf.host_name); 1.229 + 1.230 + DEBUG(5) debugf("filename = %s\n", filename); 1.231 + 1.232 + if((out = fopen(filename, "w"))){ 1.233 + gchar *newname = 1.234 + g_strdup_printf("%s/new/%s.%s", path, msg->uid, conf.host_name); 1.235 + message_stream(out, msg, hdr_list, flags); 1.236 + ok = TRUE; 1.237 + if(fflush(out) == EOF) ok = FALSE; 1.238 + else if(fdatasync(fileno(out)) != 0){ 1.239 + if(errno != EINVAL) /* some fs do not support this.. 1.240 + I hope this also means that it is not necessary */ 1.241 + ok = FALSE; 1.242 + } 1.243 + fclose(out); 1.244 + if(rename(filename, newname) != 0){ 1.245 + ok = FALSE; 1.246 + logwrite(LOG_ALERT, "moving %s to %s failed: %s", 1.247 + filename, newname, strerror(errno)); 1.248 + } 1.249 + g_free(newname); 1.250 + } 1.251 + umask(saved_mode); 1.252 + g_free(filename); 1.253 + } 1.254 + }else{ 1.255 + logwrite(LOG_ALERT, "%s is not a directory\n", path); 1.256 + errno = ENOTDIR; 1.257 + } 1.258 + } 1.259 + if(!conf.run_as_user){ 1.260 + uid_ok = (seteuid(0) == 0); 1.261 + if(uid_ok){ 1.262 + gid_ok = (setegid(saved_gid) == 0); 1.263 + uid_ok = (seteuid(saved_uid) == 0); 1.264 + } 1.265 + } 1.266 + if(!uid_ok || !gid_ok){ 1.267 + /* FIXME: if this fails we HAVE to exit, because we shall not run 1.268 + with some users id. But we do not return, and so this message 1.269 + will not be finished, so the user will get the message again 1.270 + next time a delivery is attempted... */ 1.271 + logwrite(LOG_ALERT, 1.272 + "could not set back uid or gid after local delivery: %s\n", 1.273 + strerror(errno)); 1.274 + exit(EXIT_FAILURE); 1.275 + } 1.276 + g_free(path); 1.277 + }else{ 1.278 + logwrite(LOG_ALERT, 1.279 + "could not set uid or gid for local delivery, uid = %d: %s\n", 1.280 + pw->pw_uid, strerror(errno)); 1.281 + } 1.282 + }else{ 1.283 + logwrite(LOG_ALERT, "could not find password entry for user %s\n", user); 1.284 + errno = ENOENT; /* getpwnam does not set errno correctly */ 1.285 + } 1.286 + return ok; 1.287 +} 1.288 +#endif 1.289 + 1.290 +gboolean 1.291 +pipe_out(message *msg, GList *hdr_list, address *rcpt, gchar *cmd, guint flags) 1.292 +{ 1.293 + gchar *envp[40]; 1.294 + FILE *out; 1.295 + uid_t saved_uid = geteuid(); 1.296 + gid_t saved_gid = getegid(); 1.297 + gboolean ok = FALSE; 1.298 + gint i, n; 1.299 + pid_t pid; 1.300 + void (*old_signal)(int); 1.301 + int status; 1.302 + 1.303 + /* set uid and gid to the mail ids */ 1.304 + if(!conf.run_as_user){ 1.305 + set_euidgid(conf.mail_uid, conf.mail_gid, &saved_uid, &saved_gid); 1.306 + } 1.307 + 1.308 + /* set environment */ 1.309 + { 1.310 + gint i = 0; 1.311 + address *ancestor = addr_find_ancestor(rcpt); 1.312 + 1.313 + envp[i++] = g_strdup_printf("SENDER=%s@%s", msg->return_path->local_part, msg->return_path->domain); 1.314 + envp[i++] = g_strdup_printf("SENDER_DOMAIN=%s", msg->return_path->domain); 1.315 + envp[i++] = g_strdup_printf("SENDER_LOCAL=%s", msg->return_path->local_part); 1.316 + envp[i++] = g_strdup_printf("RECEIVED_HOST=%s", msg->received_host ? msg->received_host : ""); 1.317 + 1.318 + envp[i++] = g_strdup_printf("RETURN_PATH=%s@%s", 1.319 + msg->return_path->local_part, msg->return_path->domain); 1.320 + envp[i++] = g_strdup_printf("DOMAIN=%s", ancestor->domain); 1.321 + 1.322 + envp[i++] = g_strdup_printf("LOCAL_PART=%s", ancestor->local_part); 1.323 + envp[i++] = g_strdup_printf("USER=%s", ancestor->local_part); 1.324 + envp[i++] = g_strdup_printf("LOGNAME=%s", ancestor->local_part); 1.325 + 1.326 + envp[i++] = g_strdup_printf("MESSAGE_ID=%s", msg->uid); 1.327 + envp[i++] = g_strdup_printf("QUALIFY_DOMAIN=%s", conf.host_name); 1.328 + 1.329 + envp[i] = NULL; 1.330 + n = i; 1.331 + } 1.332 + 1.333 + old_signal = signal(SIGCHLD, SIG_DFL); 1.334 + 1.335 + out = peidopen(cmd, "w", envp, &pid, conf.mail_uid, conf.mail_gid); 1.336 + if(out != NULL){ 1.337 + message_stream(out, msg, hdr_list, flags); 1.338 + 1.339 + fclose(out); 1.340 + 1.341 + waitpid(pid, &status, 0); 1.342 + 1.343 + if(WEXITSTATUS(status) != 0){ 1.344 + int exstat = WEXITSTATUS(status); 1.345 + logwrite(LOG_ALERT, "process returned %d (%s)\n", exstat, ext_strerror(1024 + exstat)); 1.346 + errno = 1024 + exstat; 1.347 + }else if(WIFSIGNALED(status)){ 1.348 + logwrite(LOG_ALERT, "process got signal %d\n", WTERMSIG(status)); 1.349 + }else 1.350 + ok = TRUE; 1.351 + 1.352 + }else 1.353 + logwrite(LOG_ALERT, "could not open pipe '%s': %s\n", cmd, strerror(errno)); 1.354 + 1.355 + signal(SIGCHLD, old_signal); 1.356 + 1.357 + /* free environment */ 1.358 + for(i = 0; i < n; i++){ 1.359 + g_free(envp[i]); 1.360 + } 1.361 + 1.362 + /* set uid and gid back */ 1.363 + if(!conf.run_as_user){ 1.364 + set_euidgid(saved_uid, saved_gid, NULL, NULL); 1.365 + } 1.366 + 1.367 + return ok; 1.368 +} 1.369 +