s/qmail 4.3.20
Next generation secure email transport
Loading...
Searching...
No Matches
qmail-authuser.c
Go to the documentation of this file.
1#include <stdio.h>
2#include <unistd.h>
3#include "global.h"
4#include "stralloc.h"
5#include "buffer.h"
6#include "auto_qmail.h"
7#include "case.h"
8#include "control.h"
9#include "constmap.h"
10#include "str.h"
11#include "fmt.h"
12#include "fd.h"
13#include "open.h"
14#include "byte.h"
15#include "scan.h"
16#include "md5.h"
17#include "hmac_md5.h"
18#include "sha1.h"
19#include "sha256.h"
20#include "pathexec.h"
21#include "prot.h"
22#include "wait.h"
23#include "sig.h"
24#include "error.h"
25#include "env.h"
26#include "qmail.h"
27#define FDAUTH 3
28#define FDGOSSIP 1
29#define SOCKET_CALL "-s"
30#define DOVECOT_SERVICE "-x"
31#define AUTHSLEEP 5
32
33extern char *crypt();
34#include <pwd.h>
35static struct passwd *pw;
36
37#include "hasspnam.h"
38#ifdef HASGETSPNAM
39#include <shadow.h>
40static struct spwd *spw;
41#endif
42
43#include "hasuserpw.h"
44#ifdef HASUSERPW
45#include <userpw.h>
46static struct userpw *upw;
47#endif
48
60buffer ba = BUFFER_INIT(write,FDAUTH,authbuf,sizeof(authbuf));
61
63stralloc authfile = {0};
64stralloc disabled = {0};
65stralloc user = {0}; // user w/o domain appended
66stralloc homedir = {0};
67stralloc shell = {0};
68
82static void exit(int fail)
83{
84 for (int i = 0; i < sizeof(authbuf); ++i) authbuf[i] = 0;
85 if (fail) sleep(AUTHSLEEP);
86 _exit(fail);
87}
88
89static int dig_ascii(char *digascii,const char *digest,const int len)
90{
91 static const char hextab[] = "0123456789abcdef";
92 int j;
93
94 for (j = 0; j < len; j++) {
95 digascii[2 * j] = hextab[digest[j] >> 4];
96 digascii[2 * j + 1] = hextab[digest[j] & 0x0f];
97 }
98 digascii[2 * len] = '\0';
99
100 return (2*j); // 2*len
101}
102
103static int auth_sha1(char *pwdhash,char *response)
104{
105 unsigned char digest[20];
106 unsigned char digascii[41];
107
108 sha1_hash(digest,response,str_len(response));
109 dig_ascii(digascii,digest,20);
110
111 return str_diffn(digascii,pwdhash,40);
112}
113
114static int auth_sha256(char *pwdhash,char *response)
115{
116 unsigned char digest[32];
117 unsigned char digascii[65];
118
119 sha256_hash(digest,response,str_len(response));
120 dig_ascii(digascii,digest,32);
121
122 return str_diffn(digascii,pwdhash,64);
123}
124
125static int auth_md5(char *pwdhash,char *response)
126{
127 MD5_CTX ctx;
128 unsigned char digest[16];
129 unsigned char digascii[33];
130
131 MD5Init(&ctx);
132 MD5Update(&ctx,response,str_len(response));
133 MD5Final(digest,&ctx);
134 dig_ascii(digascii,digest,16);
135
136 return str_diffn(digascii,pwdhash,32);
137}
138
139static int auth_hash(char *password,char *response)
140{
141 switch (str_len(password)) {
142 case 32: return auth_md5(password,response);
143 case 40: return auth_sha1(password,response);
144 case 64: return auth_sha256(password,response);
145 default: return -1;
146 }
147}
148
149static int auth_unix(char *user,char* response)
150{
151 char *encrypted = 0;
152 char *stored = 0;
153 int r = 111;
154
155 pw = getpwnam(user);
156 if (pw) {
157 stored = pw->pw_passwd;
158 if (!stralloc_copys(&homedir,pw->pw_dir)) exit(111);
159 if (!stralloc_copys(&shell,pw->pw_shell)) exit(111);
160 } else {
161 if (errno == ETXTBSY) exit(111);
162 exit(1);
163 }
164
165 if (response) {
166#ifdef HASUSERPW
167 upw = getuserpw(user);
168 if (upw)
169 stored = upw->upw_passwd;
170 else
171 if (errno == ETXTBSY) exit(111);
172#elif HASGETSPNAM
173 spw = getspnam(user);
174 if (spw)
175 stored = spw->sp_pwdp;
176 else
177 if (errno == ETXTBSY) exit(111);
178#endif
179 if (!stored || !*stored) exit(111);
180 encrypted = crypt(response,stored);
181 if (!encrypted) exit(111); // no password given (tx. M.B.)
182 r = str_diff(encrypted,stored);
183 }
184
185 if (r == 0 || !response) {
186 if (prot_gid((int) pw->pw_gid) == -1) exit(1);
187 if (prot_uid((int) pw->pw_uid) == -1) exit(1);
188 if (chdir(pw->pw_dir) == -1) exit(111);
189 }
190
191 return r;
192}
193
194static int auth_apop(unsigned char *password,unsigned char *response,unsigned char *challenge)
195{
196 MD5_CTX context;
197 unsigned char digest[16];
198 unsigned char digascii[33];
199
200 MD5Init(&context);
201 MD5Update(&context,challenge,str_len(challenge));
202 MD5Update(&context,password,str_len(password));
203 MD5Final(digest,&context);
204 dig_ascii(digascii,digest,16);
205
206 return (str_diff(digascii,response));
207}
208
209static int auth_cram(unsigned char *password,unsigned char *response,unsigned char *challenge)
210{
211 unsigned char digest[16];
212 unsigned char digascii[33];
213
214 hmac_md5(challenge,str_len(challenge),password,str_len(password),digest);
215 dig_ascii(digascii,digest,16);
216
217 return (str_diff(digascii,response) && str_diff(password,response)); // cram or plain
218}
219
220static int auth_dovecot(char *user,char *response,char *socket,char *service)
221{
222 int wstat;
223 int child;
224 char *wrapper[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
225 int i = 0;
226
227 close(FDGOSSIP); /* gossiping doveadm */
228
229 switch (child = fork()) {
230 case -1:
231 exit(111);
232 case 0:
233 wrapper[i] = "doveadm";
234 wrapper[++i] = "auth";
235 wrapper[++i] = "test";
236 if (socket) {
237 wrapper[++i] = "-a";
238 wrapper[++i] = socket;
239 }
240 if (service) {
241 wrapper[++i] = "-x";
242 wrapper[++i] = service;
243 }
244 wrapper[++i] = user;
245 wrapper[++i] = response;
246 wrapper[++i] = 0;
247
248 execvp(wrapper[0],wrapper);
249 exit(111);
250 }
251
252 if (wait_pid(&wstat,child) == -1) exit(111);
253 if (wait_crashed(wstat)) exit(111);
254 return wait_exitcode(wstat);
255}
256
257static int auth_wrapper(char *pam,char *arg1,char *arg2,char *auth,int len)
258{
259 int wstat;
260 int child;
261 int pi[2];
262 char *wrapper[4] = {0, 0, 0, 0};
263
264 if (pipe(pi) == -1) exit(111);
265 if (pi[0] != FDAUTH) exit(111);
266
267 switch (child = fork()) {
268 case -1:
269 exit(111);
270 case 0:
271 close(pi[1]);
272 if (fd_copy(FDAUTH,pi[0]) == -1) exit(111);
273 wrapper[0] = pam;
274 wrapper[1] = arg1;
275 wrapper[2] = arg2;
276 wrapper[3] = 0;
277 sig_pipedefault();
278
279 execvp(wrapper[0],wrapper);
280 exit(111);
281 }
282 close(pi[0]);
283
284 buffer_init(&ba,write,pi[1],authbuf,sizeof(authbuf));
285 if (buffer_put(&ba,auth,len) == -1) exit(111);
286 if (buffer_flush(&ba) == -1) exit(111);
287 close(pi[1]);
288
289 if (wait_pid(&wstat,child) == -1) exit(111);
290 if (wait_crashed(wstat)) exit(111);
291 return wait_exitcode(wstat);
292}
293
294int main(int argc,char * const *argv)
295{
296 char *authuser;
297 char *authpass;
298 char *response = 0;
299 char *challenge = 0;
300 char *domain = 0;
301 char *authsocket = 0;
302 char *service = 0;
303 char *program = 0;
304 char *maildirname = 0;
305 int rc = -1; /* initialise: -1; ok: 0; !ok: > 0 */
306 int authlen = 0;
307 int buflen = 0;
308 int domlen = 0;
309 int i = 0;
310 int r;
311
312 if (!argv[1]) exit(2);
313
314 if (!case_diffs(argv[1],SOCKET_CALL)) { // dovecot socket
315 if (!argv[3]) exit(2);
316 authsocket = argv[2];
317 if (!case_diffs(argv[3],DOVECOT_SERVICE)) { // ++ dovecot service
318 service = argv[4];
319 if (!argv[5]) exit(2);
320 }
321 } else if (!case_diffs(argv[1],DOVECOT_SERVICE)) { // dovecot service
322 if (!argv[3]) exit(2);
323 service = argv[2];
324 if (!case_diffs(argv[3],SOCKET_CALL)) { // ++ dovecot socket
325 if (!argv[5]) exit(2);
326 authsocket = argv[4];
327 }
328 } else if (argv[2]) { // pop or imap user with mailbox
329 if (case_starts(argv[2],"mail") || case_starts(argv[2],"mbox")) {
330 program = argv[1];
331 maildirname = argv[2];
332 }
333 }
334 env_unset("USER");
335
336 /* Read input on FDAUTH */
337
338 for (;;) {
339 do
340 r = read(FDAUTH,authbuf + buflen,sizeof(authbuf) - buflen);
341 while ((r == -1) && (errno == EINTR));
342 if (r == -1) exit(111);
343 if (r == 0) break;
344 buflen += r;
345 if (buflen >= sizeof(authbuf)) exit(2);
346 }
347 close(FDAUTH);
348
349 authuser = authbuf + i; /* username */
350 if (i == buflen) exit(2);
351 while (authbuf[i++]) /* response */
352 if (i == buflen) exit(2);
353 response = authbuf + i;
354 if (i == buflen) exit(2);
355 while (authbuf[i++]) /* challenge */
356 if (i == buflen) exit(2);
357 challenge = authbuf + i;
358
359 authlen = str_len(authuser);
360 if (!stralloc_copyb(&user,authuser,authlen)) exit(111);
361
362 if ((i = byte_rchr(authuser,authlen,'@'))) /* @domain */
363 if (i < authlen && authuser[i] == '@') {
364 domain = authuser + i;
365 domlen = str_len(domain);
366 case_lowerb(domain,domlen);
367 user.len = 0;
368 if (!stralloc_copyb(&user,authuser,i)) exit(111);
369 }
370 if (!stralloc_0(&user)) exit(111);
371 if (!env_put("USER",authuser)) exit(111);
372
373 /* Read control file users/authuser and go for checks */
374
375 if (chdir(auto_qmail) == -1) exit(110);
376
377 switch (control_readfile(&authfile,"users/authuser",0)) {
378 case -1: exit(110);
379 case 0: if (!constmap_init(&mapauthuser,"",0,1)) exit(111);
380 case 1: if (!constmap_init(&mapauthuser,authfile.s,authfile.len,1)) exit(111);
381 }
382
383 /* Check for disabled authuser/domains */
384
385 if (!stralloc_copys(&disabled,"!")) exit(111);
386 if (!stralloc_catb(&disabled,authuser,authlen)) exit(111);
387 if (constmap(&mapauthuser,disabled.s,disabled.len)) exit(1);
388
389 if (domlen) {
390 disabled.len = 0;
391 if (!stralloc_copys(&disabled,"!")) exit(111);
392 if (!stralloc_catb(&disabled,domain,domlen)) exit(111);
393 if (constmap(&mapauthuser,disabled.s,disabled.len)) exit(1);
394 }
395
396 /* Virtual and system user accounts */
397
398 authpass = constmap(&mapauthuser,authuser,authlen);
399
400 if (!authpass && domlen)
401 authpass = constmap(&mapauthuser,domain,domlen); // 1. authuser accounts
402 if (!authpass)
403 authpass = constmap(&mapauthuser,"*",1); // 2. system accounts
404 if (!authpass)
405 authpass = constmap(&mapauthuser,"@",1); // 3. virtual user accounts
406
407 if (!authpass) exit(1);
408
409 if (str_len(authpass) == 1) { // external IdP
410 switch (authpass[0]) {
411 case '?': rc = auth_unix(user.s,response); break;
412 case '+': if (maildirname)
413 rc = auth_wrapper("checkvpw",program,maildirname,authbuf,buflen);
414 else
415 rc = auth_wrapper("checkvpw","true","Maildir",authbuf,buflen); // Pseudo arg
416 break;
417 case '&': if (maildirname)
418 rc = auth_wrapper("vchkpw",program,maildirname,authbuf,buflen);
419 else
420 rc = auth_wrapper("vchkpw","true",0,authbuf,buflen);
421 break;
422 case '=': rc = auth_dovecot(authuser,response,authsocket,service);
423 break;
424 default: rc = 2;
425 break;
426 }
427 } else { // authuser file
428 switch (authpass[0]) {
429 case '%': rc = auth_hash(authpass + 1,response);
430 break;
431 default: if (maildirname) {
432 if ((rc = auth_cram(authpass,response,challenge) == 0)) break; // IMAP C/R
433 if ((rc = auth_apop(authpass,response,challenge)) == 0) {
434 auth_unix(user.s,0); // Unix environment only
435 }
436 } else rc = auth_cram(authpass,response,challenge);
437 break;
438 }
439 }
440
441 if (rc) exit(rc);
442
443 for (i = 0; i < sizeof(authbuf); ++i) authbuf[i] = 0;
444
445 if (authsocket && service) pathexec(argv + 5);
446 else if (authsocket || service) pathexec(argv + 3);
447 else pathexec(argv + 1);
448 exit(111);
449}
char auto_qmail[]
int main()
Definition: chkshsgr.c:6
int constmap_init(struct constmap *cm, char *s, int len, int flagcolon)
Definition: constmap.c:35
int control_readfile(stralloc *sa, char *fn, int flagme)
Definition: control.c:87
int stralloc_copys(stralloc *, char const *)
int dig_ascii(char *digascii, unsigned const char *digest, const int len)
Definition: dkimverify.cpp:62
void _exit(int)
void hmac_md5(unsigned char *text, int text_len, unsigned char *key, int key_len, unsigned char *digest)
Definition: hmac_md5.c:16
void MD5Init(MD5_CTX *)
Definition: md5c.c:95
void MD5Final(unsigned char[16], MD5_CTX *)
Definition: md5c.c:144
void MD5Update(MD5_CTX *, unsigned char *, unsigned int)
Definition: md5c.c:109
int prot_uid(int)
Definition: prot.c:18
int prot_gid(int)
Definition: prot.c:6
buffer ba
stralloc disabled
#define FDGOSSIP
char authbuf[BUFSIZE_AUTH]
stralloc shell
#define SOCKET_CALL
struct constmap mapauthuser
stralloc homedir
stralloc user
char * crypt()
#define FDAUTH
#define DOVECOT_SERVICE
#define AUTHSLEEP
stralloc authfile
stralloc auth
Definition: qmail-popup.c:71
SSL_CTX * ctx
Definition: qmail-remote.c:108
int j
Definition: qmail-send.c:926
int auth_cram()
Definition: qmail-smtpd.c:1574
#define BUFSIZE_AUTH
Definition: qmail.h:9
void sha1_hash(char *hash, const char *data, uint32_t len)
Definition: sha1.c:177
void sha256_hash(char *hash, const char *data, size_t len)
Definition: sha256.c:161
stralloc domain
Definition: spf.c:34
Definition: md5.h:31
void write()