1/*++ 2/* NAME 3/* maps 3 4/* SUMMARY 5/* multi-dictionary search 6/* SYNOPSIS 7/* #include <maps.h> 8/* 9/* MAPS *maps_create(title, map_names, flags) 10/* const char *title; 11/* const char *map_names; 12/* int flags; 13/* 14/* const char *maps_find(maps, key, flags) 15/* MAPS *maps; 16/* const char *key; 17/* int flags; 18/* 19/* MAPS *maps_free(maps) 20/* MAPS *maps; 21/* DESCRIPTION 22/* This module implements multi-dictionary searches. it goes 23/* through the high-level dictionary interface and does file 24/* locking. Dictionaries are opened read-only, and in-memory 25/* dictionary instances are shared. 26/* 27/* maps_create() takes list of type:name pairs and opens the 28/* named dictionaries. 29/* The result is a handle that must be specified along with all 30/* other maps_xxx() operations. 31/* See dict_open(3) for a description of flags. 32/* This includes the flags that specify preferences for search 33/* string case folding. 34/* 35/* maps_find() searches the specified list of dictionaries 36/* in the specified order for the named key. The result is in 37/* memory that is overwritten upon each call. 38/* The flags argument is either 0 or specifies a filter: 39/* for example, DICT_FLAG_FIXED | DICT_FLAG_PATTERN selects 40/* dictionaries that have fixed keys or pattern keys. 41/* 42/* maps_free() releases storage claimed by maps_create() 43/* and conveniently returns a null pointer. 44/* 45/* Arguments: 46/* .IP title 47/* String used for diagnostics. Typically one specifies the 48/* type of information stored in the lookup tables. 49/* .IP map_names 50/* Null-terminated string with type:name dictionary specifications, 51/* separated by whitespace or commas. 52/* .IP flags 53/* With maps_create(), flags that are passed to dict_open(). 54/* With maps_find(), flags that control searching behavior 55/* as documented above. 56/* .IP maps 57/* A result from maps_create(). 58/* .IP key 59/* Null-terminated string with a lookup key. Table lookup is case 60/* sensitive. 61/* DIAGNOSTICS 62/* Panic: inappropriate use; fatal errors: out of memory, unable 63/* to open database. Warnings: null string lookup result. 64/* 65/* maps_find() returns a null pointer when the requested 66/* information was not found, and logs a warning when the 67/* lookup failed due to error. The maps->error value indicates 68/* if the last lookup failed due to error. 69/* BUGS 70/* The dictionary name space is flat, so dictionary names allocated 71/* by maps_create() may collide with dictionary names allocated by 72/* other methods. 73/* 74/* This functionality could be implemented by allowing the user to 75/* specify dictionary search paths to dict_lookup() or dict_eval(). 76/* However, that would either require that the dict(3) module adopts 77/* someone else's list notation syntax, or that the dict(3) module 78/* imposes syntax restrictions onto other software, neither of which 79/* is desirable. 80/* LICENSE 81/* .ad 82/* .fi 83/* The Secure Mailer license must be distributed with this software. 84/* AUTHOR(S) 85/* Wietse Venema 86/* IBM T.J. Watson Research 87/* P.O. Box 704 88/* Yorktown Heights, NY 10598, USA 89/*--*/ 90 91/* System library. */ 92 93#include <sys_defs.h> 94#include <string.h> 95 96/* Utility library. */ 97 98#include <argv.h> 99#include <mymalloc.h> 100#include <msg.h> 101#include <dict.h> 102#include <stringops.h> 103#include <split_at.h> 104#ifdef __APPLE_OS_X_SERVER__ 105#include <pwd.h> 106#include <aod.h> 107#include <mail_params.h> 108#endif /* __APPLE_OS_X_SERVER__ */ 109 110/* Global library. */ 111 112#include "mail_conf.h" 113#include "maps.h" 114 115/* maps_create - initialize */ 116 117MAPS *maps_create(const char *title, const char *map_names, int dict_flags) 118{ 119 const char *myname = "maps_create"; 120 char *temp; 121 char *bufp; 122 static char sep[] = " \t,\r\n"; 123 MAPS *maps; 124 char *map_type_name; 125 VSTRING *map_type_name_flags; 126 DICT *dict; 127 128 /* 129 * Initialize. 130 */ 131 maps = (MAPS *) mymalloc(sizeof(*maps)); 132 maps->title = mystrdup(title); 133 maps->argv = argv_alloc(2); 134 maps->error = 0; 135 136 /* 137 * For each specified type:name pair, either register a new dictionary, 138 * or increment the reference count of an existing one. 139 */ 140 if (*map_names) { 141 bufp = temp = mystrdup(map_names); 142 map_type_name_flags = vstring_alloc(10); 143 144#define OPEN_FLAGS O_RDONLY 145 146 while ((map_type_name = mystrtok(&bufp, sep)) != 0) { 147 vstring_sprintf(map_type_name_flags, "%s(%o,%s)", 148 map_type_name, OPEN_FLAGS, 149 dict_flags_str(dict_flags)); 150 if ((dict = dict_handle(vstring_str(map_type_name_flags))) == 0) 151 dict = dict_open(map_type_name, OPEN_FLAGS, dict_flags); 152 if ((dict->flags & dict_flags) != dict_flags) 153 msg_panic("%s: map %s has flags 0%o, want flags 0%o", 154 myname, map_type_name, dict->flags, dict_flags); 155 dict_register(vstring_str(map_type_name_flags), dict); 156 argv_add(maps->argv, vstring_str(map_type_name_flags), ARGV_END); 157 } 158 myfree(temp); 159 vstring_free(map_type_name_flags); 160 } 161 return (maps); 162} 163 164/* maps_find - search a list of dictionaries */ 165 166const char *maps_find(MAPS *maps, const char *name, int flags) 167{ 168 const char *myname = "maps_find"; 169 char **map_name; 170 const char *expansion; 171 DICT *dict; 172 173 /* 174 * In case of return without map lookup (empty name or no maps). 175 */ 176 maps->error = 0; 177 178 /* 179 * Temp. workaround, for buggy callers that pass zero-length keys when 180 * given partial addresses. 181 */ 182 if (*name == 0) 183 return (0); 184 185 for (map_name = maps->argv->argv; *map_name; map_name++) { 186 if ((dict = dict_handle(*map_name)) == 0) 187 msg_panic("%s: dictionary not found: %s", myname, *map_name); 188 if (flags != 0 && (dict->flags & flags) == 0) 189 continue; 190 if ((expansion = dict_get(dict, name)) != 0) { 191 if (*expansion == 0) { 192 msg_warn("%s lookup of %s returns an empty string result", 193 maps->title, name); 194 msg_warn("%s should return NO RESULT in case of NOT FOUND", 195 maps->title); 196 maps->error = DICT_ERR_RETRY; 197 return (0); 198 } 199 if (msg_verbose) 200 msg_info("%s: %s: %s: %s = %s", myname, maps->title, 201 *map_name, name, expansion); 202#ifdef __APPLE_OS_X_SERVER__ 203 if (var_minimum_valid_uid && 204 strncmp(maps->title, "local_recipient_maps", 20) == 0 ) { 205 if ( var_use_getpwnam_ext ) { 206 uid_t a_uid = ads_get_uid(name); 207 if (a_uid > 0 && a_uid < var_minimum_valid_uid) { 208 msg_warn("recipient %s rejected: uid falls below minimum allowed: %d < %d", 209 name, a_uid, var_minimum_valid_uid); 210 return (0); 211 } 212 } else { 213 struct passwd *pwd; 214 if ((pwd = getpwnam(name)) != 0) { 215 if (pwd->pw_uid > 0 && pwd->pw_uid < var_minimum_valid_uid) { 216 msg_warn("recipient %s rejected: uid falls below minimum allowed: %d < %d", 217 name, pwd->pw_uid, var_minimum_valid_uid); 218 return (0); 219 } 220 } 221 } 222 223 if (sacl_check(name) == 0) { 224 msg_warn("recipient %s rejected: not in Mail Service ACL", name); 225 return 0; 226 } 227 } 228 229#endif /* __APPLE_OS_X_SERVER__ */ 230 return (expansion); 231#ifdef __APPLE_OS_X_SERVER__ 232 } else if (strncmp(maps->title, "virtual_alias_maps", 18) == 0 ) { 233 if (var_minimum_valid_uid) { 234 if ( var_use_getpwnam_ext ) { 235 uid_t a_uid = ads_get_uid(name); 236 if (a_uid > 0 && a_uid < var_minimum_valid_uid) { 237 msg_warn("recipient %s uid falls below minimum allowed: %d < %d", 238 name, a_uid, var_minimum_valid_uid); 239 return (0); 240 } 241 } else { 242 struct passwd *pwd; 243 if ((pwd = getpwnam(name)) != 0) { 244 if (pwd->pw_uid > 0 && pwd->pw_uid < var_minimum_valid_uid) { 245 msg_warn("recipient %s uid falls below minimum allowed: %d < %d", 246 name, pwd->pw_uid, var_minimum_valid_uid); 247 return (0); 248 } 249 } 250 } 251 } 252 253 if (sacl_check(name) == 0) { 254 if (msg_verbose) 255 msg_warn("recipient %s is not in Mail Service ACL", name); 256 return 0; 257 } 258 259 /* if virtual_alias_maps, we want to look in the directory 260 for a possible match as well */ 261 if ( var_use_getpwnam_ext ) { 262 const char *pw_nam; 263 if ((pw_nam = ads_getpwnam(name)) != NULL) { 264 if (msg_verbose) 265 msg_info("%s: %s: %s: %s = %s", myname, maps->title, 266 "ads_getpwnam", name, pw_nam); 267 return (pw_nam); 268 } 269 } else { 270 struct passwd *pwd; 271 if ((pwd = getpwnam(name)) != 0) { 272 if (msg_verbose) 273 msg_info("%s: %s: %s: %s = %s", myname, maps->title, 274 "getpwnam", name, pwd->pw_name); 275 return (pwd->pw_name); 276 } 277 } 278#endif 279 } else if ((maps->error = dict->error) != 0) { 280 msg_warn("%s:%s lookup error for \"%.100s\"", 281 dict->type, dict->name, name); 282 break; 283 } 284 } 285 if (msg_verbose) 286 msg_info("%s: %s: %s: %s", myname, maps->title, name, maps->error ? 287 "search aborted" : "not found"); 288 return (0); 289} 290 291/* maps_free - release storage */ 292 293MAPS *maps_free(MAPS *maps) 294{ 295 char **map_name; 296 297 for (map_name = maps->argv->argv; *map_name; map_name++) { 298 if (msg_verbose) 299 msg_info("maps_free: %s", *map_name); 300 dict_unregister(*map_name); 301 } 302 myfree(maps->title); 303 argv_free(maps->argv); 304 myfree((char *) maps); 305 return (0); 306} 307 308#ifdef TEST 309 310#include <vstring.h> 311#include <vstream.h> 312#include <vstring_vstream.h> 313 314int main(int argc, char **argv) 315{ 316 VSTRING *buf = vstring_alloc(100); 317 MAPS *maps; 318 const char *result; 319 320 if (argc != 2) 321 msg_fatal("usage: %s maps", argv[0]); 322 msg_verbose = 2; 323 maps = maps_create("whatever", argv[1], DICT_FLAG_LOCK); 324 325 while (vstring_fgets_nonl(buf, VSTREAM_IN)) { 326 maps->error = 99; 327 vstream_printf("\"%s\": ", vstring_str(buf)); 328 if ((result = maps_find(maps, vstring_str(buf), 0)) != 0) { 329 vstream_printf("%s\n", result); 330 } else if (maps->error != 0) { 331 vstream_printf("lookup error\n"); 332 } else { 333 vstream_printf("not found\n"); 334 } 335 vstream_fflush(VSTREAM_OUT); 336 } 337 maps_free(maps); 338 vstring_free(buf); 339 return (0); 340} 341 342#endif 343