summaryrefslogtreecommitdiffstats
path: root/realrcptto.c
diff options
context:
space:
mode:
authormanuel <manuel@mausz.at>2013-02-05 23:48:16 +0100
committermanuel <manuel@mausz.at>2013-02-05 23:48:16 +0100
commitb0c5a1bf27516611d34b6b7d56a0376e3c64171b (patch)
tree775492f51e62ed7b0dfe1eba75bfd189950c470f /realrcptto.c
parente52f8862b7bd6519055296a9b757259c0fd358d7 (diff)
downloadqmail-b0c5a1bf27516611d34b6b7d56a0376e3c64171b.tar.gz
qmail-b0c5a1bf27516611d34b6b7d56a0376e3c64171b.tar.bz2
qmail-b0c5a1bf27516611d34b6b7d56a0376e3c64171b.zip
[PATCH] realrcptto
Diffstat (limited to 'realrcptto.c')
-rw-r--r--realrcptto.c421
1 files changed, 421 insertions, 0 deletions
diff --git a/realrcptto.c b/realrcptto.c
new file mode 100644
index 0000000..7ec331a
--- /dev/null
+++ b/realrcptto.c
@@ -0,0 +1,421 @@
1#include <sys/types.h>
2#include <sys/stat.h>
3#include <unistd.h>
4#include <pwd.h>
5#include "auto_break.h"
6#include "auto_usera.h"
7#include "byte.h"
8#include "case.h"
9#include "cdb.h"
10#include "constmap.h"
11#include "error.h"
12#include "fmt.h"
13#include "open.h"
14#include "str.h"
15#include "stralloc.h"
16#include "uint32.h"
17#include "substdio.h"
18#include "env.h"
19#include "slurpclose.h"
20
21extern void die_nomem();
22extern void die_control();
23extern void die_cdb();
24extern void die_sys();
25
26static stralloc envnoathost = {0};
27static stralloc percenthack = {0};
28static stralloc locals = {0};
29static stralloc vdoms = {0};
30static struct constmap mappercenthack;
31static struct constmap maplocals;
32static struct constmap mapvdoms;
33
34static char *dash;
35static char *extension;
36static char *local;
37static struct passwd *pw;
38
39static char errbuf[128];
40static struct substdio sserr = SUBSTDIO_FDBUF(write,2,errbuf,sizeof errbuf);
41
42static char pidbuf[64];
43static char remoteipbuf[64]=" ";
44
45static int flagdenyall;
46static int flagdenyany;
47
48void realrcptto_init()
49{
50 char *x;
51
52 if (control_rldef(&envnoathost,"control/envnoathost",1,"envnoathost") != 1)
53 die_control();
54
55 if (control_readfile(&locals,"control/locals",1) != 1) die_control();
56 if (!constmap_init(&maplocals,locals.s,locals.len,0)) die_nomem();
57 switch(control_readfile(&percenthack,"control/percenthack",0)) {
58 case -1: die_control();
59 case 0: if (!constmap_init(&mappercenthack,"",0,0)) die_nomem();
60 case 1:
61 if (!constmap_init(&mappercenthack,percenthack.s,percenthack.len,0))
62 die_nomem();
63 }
64 switch(control_readfile(&vdoms,"control/virtualdomains",0)) {
65 case -1: die_control();
66 case 0: if (!constmap_init(&mapvdoms,"",0,1)) die_nomem();
67 case 1: if (!constmap_init(&mapvdoms,vdoms.s,vdoms.len,1)) die_nomem();
68 }
69
70 str_copy(pidbuf + fmt_ulong(pidbuf,getpid())," ");
71 x=env_get("PROTO");
72 if (x) {
73 static char const remoteip[]="REMOTEIP";
74 unsigned int len = str_len(x);
75 if (len <= sizeof remoteipbuf - sizeof remoteip) {
76 byte_copy(remoteipbuf,len,x);
77 byte_copy(remoteipbuf + len,sizeof remoteip,remoteip);
78 x = env_get(remoteipbuf);
79 len = str_len(x);
80 if (len + 1 < sizeof remoteipbuf) {
81 byte_copy(remoteipbuf,len,x);
82 remoteipbuf[len]=' ';
83 remoteipbuf[len + 1]='\0';
84 }
85 }
86 }
87
88 x = env_get("QMAILRRTDENYALL");
89 flagdenyall = (x && x[0]=='1' && x[1]=='\0');
90}
91
92void realrcptto_start()
93{
94 flagdenyany = 0;
95}
96
97static int denyaddr(addr, depth)
98char *addr;
99int depth;
100{
101 if (depth == 1)
102 {
103 substdio_puts(&sserr,"realrcptto ");
104 substdio_puts(&sserr,pidbuf);
105 substdio_puts(&sserr,remoteipbuf);
106 substdio_puts(&sserr,addr);
107 substdio_puts(&sserr,"\n");
108 substdio_flush(&sserr);
109 flagdenyany = 1;
110 }
111 return flagdenyall;
112}
113
114static void stat_error(path,error)
115char* path;
116int error;
117{
118 substdio_puts(&sserr,"unable to stat ");
119 substdio_puts(&sserr,path);
120 substdio_puts(&sserr,": ");
121 substdio_puts(&sserr,error_str(error));
122 substdio_puts(&sserr,"\n");
123 substdio_flush(&sserr);
124}
125
126#define GETPW_USERLEN 32
127
128static int userext()
129{
130 char username[GETPW_USERLEN];
131 struct stat st;
132
133 extension = local + str_len(local);
134 for (;;) {
135 if (extension - local < sizeof(username))
136 if (!*extension || (*extension == *auto_break)) {
137 byte_copy(username,extension - local,local);
138 username[extension - local] = 0;
139 case_lowers(username);
140 errno = 0;
141 pw = getpwnam(username);
142 if (errno == error_txtbsy) die_sys();
143 if (pw)
144 if (pw->pw_uid)
145 if (stat(pw->pw_dir,&st) == 0) {
146 if (st.st_uid == pw->pw_uid) {
147 dash = "";
148 if (*extension) { ++extension; dash = "-"; }
149 return 1;
150 }
151 }
152 else
153 if (error_temp(errno)) die_sys();
154 }
155 if (extension == local) return 0;
156 --extension;
157 }
158}
159
160// max lookups
161#define MAXRECURSION 5
162
163static int handleqme(qme, depth)
164stralloc *qme;
165int depth;
166{
167 stralloc cmds = {0};
168 char *username = NULL;
169 int fd, i, j, k, count;
170
171 if (depth >= MAXRECURSION) return 0;
172 if (!stralloc_ready(&cmds,0)) die_nomem();
173 cmds.len = 0;
174
175 fd = open_read(qme->s);
176 if (fd == -1) return 0;
177 if (slurpclose(fd,&cmds,256) == -1) die_nomem();
178 if (!cmds.len || (cmds.s[cmds.len - 1] != '\n'))
179 if (!stralloc_cats(&cmds,"\n")) die_nomem();
180
181 i = count = 0;
182 for (j = 0;j < cmds.len;++j)
183 {
184 if (cmds.s[j] == '\n')
185 {
186 cmds.s[j] = 0;
187 k = j;
188 while ((k > i) && ((cmds.s[k - 1] == ' ') || (cmds.s[k - 1] == '\t')))
189 cmds.s[--k] = 0;
190 switch(cmds.s[i])
191 {
192 case 0:
193 case '#':
194 case '.':
195 case '/':
196 case '|':
197 case '+':
198 break;
199 case '&':
200 ++i;
201 default:
202 count++;
203 username = cmds.s + i;
204 break;
205 }
206 i = j + 1;
207 }
208 }
209
210 if (count == 1 && username)
211 realrcptto(username, ++depth);
212
213 return 1;
214}
215
216int realrcptto(addr, depth)
217char *addr;
218int depth;
219{
220 env_unset("DTUSER");
221 return realrcptto_ex(addr, depth);
222}
223
224int realrcptto_ex(addr, depth)
225char *addr;
226int depth;
227{
228 char *homedir, *username;
229 static stralloc localpart = {0};
230 static stralloc lower = {0};
231 static stralloc nughde = {0};
232 static stralloc wildchars = {0};
233 static stralloc safeext = {0};
234 static stralloc qme = {0};
235 unsigned int i,at;
236
237 /* Short circuit, or full logging? Short circuit. */
238 if (flagdenyall && flagdenyany) return 1;
239
240 /* qmail-send:rewrite */
241 if (!stralloc_copys(&localpart,addr)) die_nomem();
242 i = byte_rchr(localpart.s,localpart.len,'@');
243 if (i == localpart.len) {
244 if (!stralloc_cats(&localpart,"@")) die_nomem();
245 if (!stralloc_cat(&localpart,&envnoathost)) die_nomem();
246 }
247 while (constmap(&mappercenthack,localpart.s + i + 1,localpart.len - i - 1)) {
248 unsigned int j = byte_rchr(localpart.s,i,'%');
249 if (j == i) break;
250 localpart.len = i;
251 i = j;
252 localpart.s[i] = '@';
253 }
254 at = byte_rchr(localpart.s,localpart.len,'@');
255 if (constmap(&maplocals,localpart.s + at + 1,localpart.len - at - 1)) {
256 localpart.len = at;
257 localpart.s[at] = '\0';
258 } else {
259 unsigned int xlen,newlen;
260 char *x;
261 for (i = 0;;++i) {
262 if (i > localpart.len) return denyaddr(addr, depth);
263 if (!i || (i == at + 1) || (i == localpart.len) ||
264 ((i > at) && (localpart.s[i] == '.'))) {
265 x = constmap(&mapvdoms,localpart.s + i,localpart.len - i);
266 if (x && i == at + 1) {
267 // set QMAILQUEUE if catch-all
268 char *qmailqueue;
269 qmailqueue = env_get("QMAILQUEUE");
270 if (qmailqueue) {
271 if (!env_put("QMAILQUEUE=bin/qmail-queue")) die_nomem();
272 }
273 }
274 if (x) break;
275 }
276 }
277 if (!*x) return 1;
278 xlen = str_len(x) + 1; /* +1 for '-' */
279 newlen = xlen + at + 1; /* +1 for \0 */
280 if (xlen < 1 || newlen - 1 < xlen || newlen < 1 ||
281 !stralloc_ready(&localpart,newlen))
282 die_nomem();
283 localpart.s[newlen - 1] = '\0';
284 byte_copyr(localpart.s + xlen,at,localpart.s);
285 localpart.s[xlen - 1] = '-';
286 byte_copy(localpart.s,xlen - 1,x);
287 localpart.len = newlen;
288 }
289
290 /* qmail-lspawn:nughde_get */
291 {
292 int r,fd,flagwild;
293 if (!stralloc_copys(&lower,"!")) die_nomem();
294 if (!stralloc_cats(&lower,localpart.s)) die_nomem();
295 if (!stralloc_0(&lower)) die_nomem();
296 case_lowerb(lower.s,lower.len);
297 if (!stralloc_copys(&nughde,"")) die_nomem();
298 fd = open_read("users/cdb");
299 if (fd == -1) {
300 if (errno != error_noent) die_cdb();
301 } else {
302 uint32 dlen;
303 r = cdb_seek(fd,"",0,&dlen);
304 if (r != 1) die_cdb();
305 if (!stralloc_ready(&wildchars,(unsigned int) dlen)) die_nomem();
306 wildchars.len = dlen;
307 if (cdb_bread(fd,wildchars.s,wildchars.len) == -1) die_cdb();
308 i = lower.len;
309 flagwild = 0;
310 do { /* i > 0 */
311 if (!flagwild || (i == 1) ||
312 (byte_chr(wildchars.s,wildchars.len,lower.s[i - 1])
313 < wildchars.len)) {
314 r = cdb_seek(fd,lower.s,i,&dlen);
315 if (r == -1) die_cdb();
316 if (r == 1) {
317 char *x;
318 if (!stralloc_ready(&nughde,(unsigned int) dlen)) die_nomem();
319 nughde.len = dlen;
320 if (cdb_bread(fd,nughde.s,nughde.len) == -1) die_cdb();
321 if (flagwild)
322 if (!stralloc_cats(&nughde,localpart.s + i - 1)) die_nomem();
323 if (!stralloc_0(&nughde)) die_nomem();
324 close(fd);
325 x=nughde.s;
326 /* skip username */
327 username=x;
328 x += byte_chr(x,nughde.s + nughde.len - x,'\0');
329 if (x == nughde.s + nughde.len) return 1;
330 ++x;
331 /* skip uid */
332 x += byte_chr(x,nughde.s + nughde.len - x,'\0');
333 if (x == nughde.s + nughde.len) return 1;
334 ++x;
335 /* skip gid */
336 x += byte_chr(x,nughde.s + nughde.len - x,'\0');
337 if (x == nughde.s + nughde.len) return 1;
338 ++x;
339 /* skip homedir */
340 homedir=x;
341 x += byte_chr(x,nughde.s + nughde.len - x,'\0');
342 if (x == nughde.s + nughde.len) return 1;
343 ++x;
344 /* skip dash */
345 dash=x;
346 x += byte_chr(x,nughde.s + nughde.len - x,'\0');
347 if (x == nughde.s + nughde.len) return 1;
348 ++x;
349 extension=x;
350 goto got_nughde;
351 }
352 }
353 --i;
354 flagwild = 1;
355 } while (i);
356 close(fd);
357 }
358 }
359
360 /* qmail-getpw */
361 local = localpart.s;
362 if (!userext()) {
363 extension = local;
364 dash = "-";
365 pw = getpwnam(auto_usera);
366 }
367 if (!pw) return denyaddr(addr, depth);
368 if (!stralloc_copys(&nughde,pw->pw_dir)) die_nomem();
369 if (!stralloc_0(&nughde)) die_nomem();
370 homedir=nughde.s;
371 username=pw->pw_name;
372
373 got_nughde:
374
375 /* qmail-local:qmesearch */
376 //if (!*dash) return 1;
377 if (!*dash) { env_put2("DTUSER", username); return 1; }
378 if (!stralloc_copys(&safeext,extension)) die_nomem();
379 case_lowerb(safeext.s,safeext.len);
380 for (i = 0;i < safeext.len;++i)
381 {
382 if (safeext.s[i] == '.')
383 safeext.s[i] = ':';
384 }
385 {
386 struct stat st;
387 int i;
388 if (!stralloc_copys(&qme,homedir)) die_nomem();
389 if (!stralloc_cats(&qme,"/.qmail")) die_nomem();
390 if (!stralloc_cats(&qme,dash)) die_nomem();
391 if (!stralloc_cat(&qme,&safeext)) die_nomem();
392 if (!stralloc_0(&qme)) die_nomem();
393 //if (stat(qme.s,&st) == 0) return 1;
394 if (stat(qme.s,&st) == 0) { handleqme(&qme, depth); return 1; }
395 if (errno != error_noent) {
396 stat_error(qme.s,errno);
397 return 1;
398 }
399 for (i = safeext.len;i >= 0;--i)
400 if (!i || (safeext.s[i - 1] == '-')) {
401 if (!stralloc_copys(&qme,homedir)) die_nomem();
402 if (!stralloc_cats(&qme,"/.qmail")) die_nomem();
403 if (!stralloc_cats(&qme,dash)) die_nomem();
404 if (!stralloc_catb(&qme,safeext.s,i)) die_nomem();
405 if (!stralloc_cats(&qme,"default")) die_nomem();
406 if (!stralloc_0(&qme)) die_nomem();
407 //if (stat(qme.s,&st) == 0) return 1;
408 if (stat(qme.s,&st) == 0) { handleqme(&qme, depth); return 1; }
409 if (errno != error_noent) {
410 stat_error(qme.s,errno);
411 return 1;
412 }
413 }
414 return denyaddr(addr, depth);
415 }
416}
417
418int realrcptto_deny()
419{
420 return flagdenyall && flagdenyany;
421}