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