Postfix3.3.1
smtp_sasl_auth_cache.c
[詳解]
1 /*++
2 /* NAME
3 /* smtp_sasl_auth_cache 3
4 /* SUMMARY
5 /* Postfix SASL authentication reply cache
6 /* SYNOPSIS
7 /* #include "smtp.h"
8 /* #include "smtp_sasl_auth_cache.h"
9 /*
10 /* SMTP_SASL_AUTH_CACHE *smtp_sasl_auth_cache_init(map, ttl)
11 /* const char *map
12 /* int ttl;
13 /*
14 /* void smtp_sasl_auth_cache_store(auth_cache, session, resp)
15 /* SMTP_SASL_AUTH_CACHE *auth_cache;
16 /* const SMTP_SESSION *session;
17 /* const SMTP_RESP *resp;
18 /*
19 /* int smtp_sasl_auth_cache_find(auth_cache, session)
20 /* SMTP_SASL_AUTH_CACHE *auth_cache;
21 /* const SMTP_SESSION *session;
22 /*
23 /* char *smtp_sasl_auth_cache_dsn(auth_cache)
24 /* SMTP_SASL_AUTH_CACHE *auth_cache;
25 /*
26 /* char *smtp_sasl_auth_cache_text(auth_cache)
27 /* SMTP_SASL_AUTH_CACHE *auth_cache;
28 /* DESCRIPTION
29 /* This module maintains a cache of SASL authentication server replies.
30 /* This can be used to avoid repeated login failure errors.
31 /*
32 /* smtp_sasl_auth_cache_init() opens or creates the named cache.
33 /*
34 /* smtp_sasl_auth_cache_store() stores information about a
35 /* SASL login attempt together with the server status and
36 /* complete response.
37 /*
38 /* smtp_sasl_auth_cache_find() returns non-zero when a cache
39 /* entry exists for the given host, username and password.
40 /*
41 /* smtp_sasl_auth_cache_dsn() and smtp_sasl_auth_cache_text()
42 /* return the status and complete server response as found
43 /* with smtp_sasl_auth_cache_find().
44 /*
45 /* Arguments:
46 /* .IP map
47 /* Lookup table name. The name must be singular and must start
48 /* with "proxy:".
49 /* .IP ttl
50 /* The time after which a cache entry is considered expired.
51 /* .IP session
52 /* Session context.
53 /* .IP resp
54 /* Remote SMTP server response, to be stored into the cache.
55 /* DIAGNOSTICS
56 /* All errors are fatal.
57 /* LICENSE
58 /* .ad
59 /* .fi
60 /* The Secure Mailer license must be distributed with this software.
61 /* AUTHOR(S)
62 /* Original author:
63 /* Keean Schupke
64 /* Fry-IT Ltd.
65 /*
66 /* Updated by:
67 /* Wietse Venema
68 /* IBM T.J. Watson Research
69 /* P.O. Box 704
70 /* Yorktown Heights, NY 10598, USA
71 /*--*/
72 
73  /*
74  * System library.
75  */
76 #include <sys_defs.h>
77 
78  /*
79  * Utility library
80  */
81 #include <msg.h>
82 #include <mymalloc.h>
83 #include <stringops.h>
84 #include <base64_code.h>
85 #include <dict.h>
86 
87  /*
88  * Global library
89  */
90 #include <dsn_util.h>
91 #include <dict_proxy.h>
92 
93  /*
94  * Application-specific
95  */
96 #include "smtp.h"
97 #include "smtp_sasl_auth_cache.h"
98 
99  /*
100  * XXX This feature stores passwords, so we must mask them with a strong
101  * cryptographic hash. This requires OpenSSL support.
102  *
103  * XXX It would be even better if the stored hash were salted.
104  */
105 #ifdef HAVE_SASL_AUTH_CACHE
106 
107 /* smtp_sasl_auth_cache_init - per-process initialization (pre jail) */
108 
109 SMTP_SASL_AUTH_CACHE *smtp_sasl_auth_cache_init(const char *map, int ttl)
110 {
111  const char *myname = "smtp_sasl_auth_cache_init";
112  SMTP_SASL_AUTH_CACHE *auth_cache;
113 
114  /*
115  * Sanity checks.
116  */
117 #define HAS_MULTIPLE_VALUES(s) ((s)[strcspn((s), CHARS_COMMA_SP)] != 0)
118 
119  if (*map == 0)
120  msg_panic("%s: empty SASL authentication cache name", myname);
121  if (ttl < 0)
122  msg_panic("%s: bad SASL authentication cache ttl: %d", myname, ttl);
123  if (HAS_MULTIPLE_VALUES(map))
124  msg_fatal("SASL authentication cache name \"%s\" "
125  "contains multiple values", map);
126 
127  /*
128  * XXX To avoid multiple writers the map needs to be maintained by the
129  * proxywrite service. We would like to have a DICT_FLAG_REQ_PROXY flag
130  * so that the library can enforce this, but that requires moving the
131  * dict_proxy module one level down in the build dependency hierarchy.
132  */
133 #define CACHE_DICT_OPEN_FLAGS \
134  (DICT_FLAG_DUP_REPLACE | DICT_FLAG_SYNC_UPDATE | DICT_FLAG_UTF8_REQUEST)
135 #define PROXY_COLON DICT_TYPE_PROXY ":"
136 #define PROXY_COLON_LEN (sizeof(PROXY_COLON) - 1)
137 
138  if (strncmp(map, PROXY_COLON, PROXY_COLON_LEN) != 0)
139  msg_fatal("SASL authentication cache name \"%s\" must start with \""
140  PROXY_COLON, map);
141 
142  auth_cache = (SMTP_SASL_AUTH_CACHE *) mymalloc(sizeof(*auth_cache));
143  auth_cache->dict = dict_open(map, O_CREAT | O_RDWR, CACHE_DICT_OPEN_FLAGS);
144  auth_cache->ttl = ttl;
145  auth_cache->dsn = mystrdup("");
146  auth_cache->text = mystrdup("");
147  return (auth_cache);
148 }
149 
150  /*
151  * Each cache lookup key contains a server host name and user name. Each
152  * cache value contains a time stamp, a hashed password, and the server
153  * response. With this organization, we don't have to worry about cache
154  * pollution, because we can detect if a cache entry has expired, or if the
155  * password has changed.
156  */
157 
158 /* smtp_sasl_auth_cache_make_key - format auth failure cache lookup key */
159 
160 static char *smtp_sasl_auth_cache_make_key(const char *host, const char *user)
161 {
162  VSTRING *buf = vstring_alloc(100);
163 
164  vstring_sprintf(buf, "%s;%s", host, user);
165  return (vstring_export(buf));
166 }
167 
168 /* smtp_sasl_auth_cache_make_pass - hash the auth failure cache password */
169 
170 static char *smtp_sasl_auth_cache_make_pass(const char *password)
171 {
172  VSTRING *buf = vstring_alloc(2 * SHA_DIGEST_LENGTH);
173 
174  base64_encode(buf, (const char *) SHA1((const unsigned char *) password,
175  strlen(password), 0),
176  SHA_DIGEST_LENGTH);
177  return (vstring_export(buf));
178 }
179 
180 /* smtp_sasl_auth_cache_make_value - format auth failure cache value */
181 
182 static char *smtp_sasl_auth_cache_make_value(const char *password,
183  const char *dsn,
184  const char *rep_str)
185 {
186  VSTRING *val_buf = vstring_alloc(100);
187  char *pwd_hash;
188  unsigned long now = (unsigned long) time((time_t *) 0);
189 
190  pwd_hash = smtp_sasl_auth_cache_make_pass(password);
191  vstring_sprintf(val_buf, "%lu;%s;%s;%s", now, pwd_hash, dsn, rep_str);
192  myfree(pwd_hash);
193  return (vstring_export(val_buf));
194 }
195 
196 /* smtp_sasl_auth_cache_valid_value - validate auth failure cache value */
197 
198 static int smtp_sasl_auth_cache_valid_value(SMTP_SASL_AUTH_CACHE *auth_cache,
199  const char *entry,
200  const char *password)
201 {
202  ssize_t len = strlen(entry);
203  char *cache_hash = mymalloc(len);
204  char *curr_hash;
205  unsigned long now = (unsigned long) time((time_t *) 0);
206  unsigned long time_stamp;
207  int valid;
208 
209  auth_cache->dsn = myrealloc(auth_cache->dsn, len);
210  auth_cache->text = myrealloc(auth_cache->text, len);
211 
212  if (sscanf(entry, "%lu;%[^;];%[^;];%[^\n]", &time_stamp, cache_hash,
213  auth_cache->dsn, auth_cache->text) != 4
214  || !dsn_valid(auth_cache->dsn)) {
215  msg_warn("bad smtp_sasl_auth_cache entry: %.100s", entry);
216  valid = 0;
217  } else if (time_stamp + auth_cache->ttl < now) {
218  valid = 0;
219  } else {
220  curr_hash = smtp_sasl_auth_cache_make_pass(password);
221  valid = (strcmp(cache_hash, curr_hash) == 0);
222  myfree(curr_hash);
223  }
224  myfree(cache_hash);
225  return (valid);
226 }
227 
228 /* smtp_sasl_auth_cache_find - search auth failure cache */
229 
230 int smtp_sasl_auth_cache_find(SMTP_SASL_AUTH_CACHE *auth_cache,
231  const SMTP_SESSION *session)
232 {
233  SMTP_ITERATOR *iter = session->iterator;
234  char *key;
235  const char *entry;
236  int valid = 0;
237 
238  key = smtp_sasl_auth_cache_make_key(STR(iter->host), session->sasl_username);
239  if ((entry = dict_get(auth_cache->dict, key)) != 0)
240  if ((valid = smtp_sasl_auth_cache_valid_value(auth_cache, entry,
241  session->sasl_passwd)) == 0)
242  /* Remove expired, password changed, or malformed cache entry. */
243  if (dict_del(auth_cache->dict, key) != 0)
244  msg_warn("SASL auth failure map %s: entry not deleted: %s",
245  auth_cache->dict->name, key);
246  if (auth_cache->dict->error)
247  msg_warn("SASL auth failure map %s: lookup failed for %s",
248  auth_cache->dict->name, key);
249  myfree(key);
250  return (valid);
251 }
252 
253 /* smtp_sasl_auth_cache_store - update auth failure cache */
254 
255 void smtp_sasl_auth_cache_store(SMTP_SASL_AUTH_CACHE *auth_cache,
256  const SMTP_SESSION *session,
257  const SMTP_RESP *resp)
258 {
259  SMTP_ITERATOR *iter = session->iterator;
260  char *key;
261  char *value;
262 
263  key = smtp_sasl_auth_cache_make_key(STR(iter->host), session->sasl_username);
264  value = smtp_sasl_auth_cache_make_value(session->sasl_passwd,
265  resp->dsn, resp->str);
266  dict_put(auth_cache->dict, key, value);
267 
268  myfree(value);
269  myfree(key);
270 }
271 
272 #endif
void myfree(void *ptr)
Definition: mymalloc.c:207
char * str
Definition: smtp.h:500
#define PROXY_COLON
size_t dsn_valid(const char *text)
Definition: dsn_util.c:112
char * mystrdup(const char *str)
Definition: mymalloc.c:225
#define dict_put(dp, key, val)
Definition: dict.h:237
NORETURN msg_panic(const char *fmt,...)
Definition: msg.c:295
const char * dsn
Definition: smtp.h:499
void * myrealloc(void *ptr, ssize_t len)
Definition: mymalloc.c:175
DICT * dict_open(const char *, int, int)
Definition: dict_open.c:421
#define dict_get(dp, key)
Definition: dict.h:236
#define PROXY_COLON_LEN
VSTRING * host
Definition: smtp.h:54
#define STR(x)
Definition: anvil.c:518
void msg_warn(const char *fmt,...)
Definition: msg.c:215
VSTRING * vstring_alloc(ssize_t len)
Definition: vstring.c:353
void const char long
Definition: opened.h:22
VSTRING * vstring_sprintf(VSTRING *vp, const char *format,...)
Definition: vstring.c:602
NORETURN msg_fatal(const char *fmt,...)
Definition: msg.c:249
VSTRING * base64_encode(VSTRING *, const char *, ssize_t)
Definition: base64_code.c:90
#define dict_del(dp, key)
Definition: dict.h:238
char * vstring_export(VSTRING *vp)
Definition: vstring.c:569
void * mymalloc(ssize_t len)
Definition: mymalloc.c:150
SMTP_ITERATOR * iterator
Definition: smtp.h:306