/* Copyright (c) 2002 Aaron Stone, aaron@serendipity.cx Copyright (c) 2005 Paul Stevens, paul@nfg.nl This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* User authentication functions for LDAP. */ #include "dbmail.h" #define THIS_MODULE "auth" #define AUTH_QUERY_SIZE 1024 #define LDAP_RES_SIZE 1024 extern char *configFile; static LDAP *_ldap_conn = NULL; LDAPMessage *_ldap_res; LDAPMessage *_ldap_msg; int _ldap_err; int _ldap_attrsonly = 0; char *_ldap_dn; char **_ldap_vals; char **_ldap_attrs = NULL; char _ldap_query[AUTH_QUERY_SIZE]; typedef struct _ldap_cfg { field_t bind_dn, bind_pw, base_dn, port, uri, version, scope, hostname; field_t user_objectclass, forw_objectclass; field_t cn_string; field_t field_uid, field_cid, min_cid, max_cid, field_nid, min_nid, max_nid; field_t field_mail, field_mailalt, mailaltprefix; field_t field_maxmail, field_passwd; field_t field_fwd, field_fwdsave, field_fwdtarget, fwdtargetprefix; field_t field_members; int scope_int, port_int, version_int; } _ldap_cfg_t; _ldap_cfg_t _ldap_cfg; static GList * __auth_get_every_match(const char *q, char **retfields); static int dm_ldap_user_shadow_rename(u64_t user_idnr, const char *new_name); static int auth_reconnect(void); static int auth_search(const gchar *query); static void __auth_get_config(void) { static int beenhere=0; if (beenhere) return; GETCONFIGVALUE("BIND_DN", "LDAP", _ldap_cfg.bind_dn); GETCONFIGVALUE("BIND_PW", "LDAP", _ldap_cfg.bind_pw); GETCONFIGVALUE("BASE_DN", "LDAP", _ldap_cfg.base_dn); GETCONFIGVALUE("PORT", "LDAP", _ldap_cfg.port); GETCONFIGVALUE("URI", "LDAP", _ldap_cfg.uri); GETCONFIGVALUE("VERSION", "LDAP", _ldap_cfg.version); GETCONFIGVALUE("HOSTNAME", "LDAP", _ldap_cfg.hostname); GETCONFIGVALUE("USER_OBJECTCLASS", "LDAP", _ldap_cfg.user_objectclass); GETCONFIGVALUE("FORW_OBJECTCLASS", "LDAP", _ldap_cfg.forw_objectclass); GETCONFIGVALUE("CN_STRING", "LDAP", _ldap_cfg.cn_string); GETCONFIGVALUE("FIELD_UID", "LDAP", _ldap_cfg.field_uid); GETCONFIGVALUE("FIELD_CID", "LDAP", _ldap_cfg.field_cid); GETCONFIGVALUE("MIN_CID", "LDAP", _ldap_cfg.min_cid); GETCONFIGVALUE("MAX_CID", "LDAP", _ldap_cfg.max_cid); GETCONFIGVALUE("FIELD_NID", "LDAP", _ldap_cfg.field_nid); GETCONFIGVALUE("MIN_NID", "LDAP", _ldap_cfg.min_nid); GETCONFIGVALUE("MAX_NID", "LDAP", _ldap_cfg.max_nid); GETCONFIGVALUE("FIELD_MAIL", "LDAP", _ldap_cfg.field_mail); GETCONFIGVALUE("FIELD_QUOTA", "LDAP", _ldap_cfg.field_maxmail); GETCONFIGVALUE("FIELD_PASSWD", "LDAP", _ldap_cfg.field_passwd); GETCONFIGVALUE("FIELD_FWDTARGET", "LDAP", _ldap_cfg.field_fwdtarget); GETCONFIGVALUE("SCOPE", "LDAP", _ldap_cfg.scope); /* Store the port as an integer for later use. */ _ldap_cfg.port_int = atoi(_ldap_cfg.port); /* Store the version as an integer for later use. */ _ldap_cfg.version_int = atoi(_ldap_cfg.version); /* defaults to version 3 */ if (!_ldap_cfg.version_int) _ldap_cfg.version_int=3; /* Compare the input string with the possible options, * making sure not to exceeed the length of the given string */ { int len = (strlen(_ldap_cfg.scope) < 3 ? strlen(_ldap_cfg.scope) : 3); if (strncasecmp(_ldap_cfg.scope, "one", len) == 0) _ldap_cfg.scope_int = LDAP_SCOPE_ONELEVEL; else if (strncasecmp(_ldap_cfg.scope, "bas", len) == 0) _ldap_cfg.scope_int = LDAP_SCOPE_BASE; else if (strncasecmp(_ldap_cfg.scope, "sub", len) == 0) _ldap_cfg.scope_int = LDAP_SCOPE_SUBTREE; else _ldap_cfg.scope_int = LDAP_SCOPE_SUBTREE; } TRACE(TRACE_DEBUG, "integer ldap scope is [%d]", _ldap_cfg.scope_int); beenhere++; } static int auth_ldap_bind(void) { TRACE(TRACE_DEBUG, "binding to ldap server as [%s] / [xxxxxxxx]", _ldap_cfg.bind_dn); /* * TODO: support tls connects */ if ((_ldap_err = ldap_bind_s(_ldap_conn, _ldap_cfg.bind_dn, _ldap_cfg.bind_pw, LDAP_AUTH_SIMPLE))) { TRACE(TRACE_ERROR, "ldap_bind_s failed: %s", ldap_err2string(_ldap_err)); return -1; } TRACE(TRACE_DEBUG, "successfully bound to ldap server" ); return 0; } /* * auth_connect() * * initializes the connection for authentication. * * returns 0 on success, -1 on failure */ int auth_connect(void) { int version = 0; #ifdef HAVE_LDAP_INITIALIZE int ret; char *uri; #endif if (_ldap_conn != NULL) return 0; __auth_get_config(); switch (_ldap_cfg.version_int) { case 3: version = LDAP_VERSION3; if (strlen(_ldap_cfg.uri)) { #ifdef HAVE_LDAP_INITIALIZE TRACE(TRACE_DEBUG, "connecting to ldap server on [%s] version [%d]", _ldap_cfg.uri, _ldap_cfg.version_int); if ((ret = ldap_initialize(&_ldap_conn, _ldap_cfg.uri) == LDAP_SUCCESS)) break; else TRACE(TRACE_WARNING, "ldap_initialize() returned %d", ret); #else TRACE(TRACE_WARNING, "LDAP library doesn't support ldap_initialize()." " You cannot use the URI directive to specify the DSN."); #endif } #ifdef HAVE_LDAP_INITIALIZE uri = g_strdup_printf("ldap://%s:%d", _ldap_cfg.hostname, _ldap_cfg.port_int); TRACE(TRACE_DEBUG, "connecting to ldap server on [%s] version [%d]", uri, _ldap_cfg.version_int); if ((ret = ldap_initialize(&_ldap_conn, uri)) != LDAP_SUCCESS) TRACE(TRACE_FATAL, "ldap_initialize() returned [%d]", ret); g_free(uri); #else TRACE(TRACE_DEBUG, "connecting to ldap server on [%s] : [%d] version [%d]", _ldap_cfg.hostname, _ldap_cfg.port_int, _ldap_cfg.version_int); _ldap_conn = ldap_init(_ldap_cfg.hostname, _ldap_cfg.port_int); #endif break; case 2: version = LDAP_VERSION2; /* fall through... */ default: if (!version) { TRACE(TRACE_WARNING, "Unsupported LDAP version [%d] requested." " Default to LDAP version 3.", _ldap_cfg.version_int); version = LDAP_VERSION3; } TRACE(TRACE_DEBUG, "connecting to ldap server on [%s] : [%d] version [%d]", _ldap_cfg.hostname, _ldap_cfg.port_int, _ldap_cfg.version_int); _ldap_conn = ldap_init(_ldap_cfg.hostname, _ldap_cfg.port_int); break; } ldap_set_option(_ldap_conn, LDAP_OPT_PROTOCOL_VERSION, &version); return auth_ldap_bind(); } int auth_disconnect(void) { /* Destroy the connection */ if (_ldap_conn != NULL) { /* If LDAP server has gone, we will catch a SIGPIPE in * ldap_unbind... we should ignore it. */ struct sigaction act, oldact; memset(&act, 0, sizeof(act)); memset(&oldact, 0, sizeof(oldact)); act.sa_handler = SIG_IGN; sigaction(SIGPIPE, &act, &oldact); ldap_unbind(_ldap_conn); _ldap_conn = NULL; sigaction(SIGPIPE, &oldact, 0); } return 0; } static int auth_search(const gchar *query) { int c=0; g_return_val_if_fail(query!=NULL, DM_EQUERY); while (c++ < 5) { TRACE(TRACE_DEBUG, " [%s]", query); _ldap_err = ldap_search_s(_ldap_conn, _ldap_cfg.base_dn, _ldap_cfg.scope_int, query, _ldap_attrs, _ldap_attrsonly, &_ldap_res); if (! _ldap_err) return 0; switch (_ldap_err) { case LDAP_SERVER_DOWN: TRACE(TRACE_WARNING, "LDAP gone away: %s. Try to reconnect(%d/5).", ldap_err2string(_ldap_err),c); if (auth_reconnect()) sleep(2); // reconnect failed. wait before trying again break; default: TRACE(TRACE_ERROR, "LDAP error(%d): %s", _ldap_err, ldap_err2string(_ldap_err)); return _ldap_err; break; } } TRACE(TRACE_FATAL,"unrecoverable error while talking to ldap server"); return -1; } static int auth_reconnect(void) { auth_disconnect(); return auth_connect(); } void dm_ldap_freeresult(GList *entlist) { GList *fldlist, *attlist; entlist = g_list_first(entlist); while (entlist) { fldlist = entlist->data; while(fldlist) { attlist = fldlist->data; g_list_destroy(attlist); if (! g_list_next(fldlist)) break; fldlist = g_list_next(fldlist); } g_list_free(g_list_first(fldlist)); if (! g_list_next(entlist)) break; entlist = g_list_next(entlist); } g_list_free(g_list_first(entlist)); } GList * dm_ldap_entdm_list_get_values(GList *entlist) { GList *fldlist, *attlist; GList *values = NULL; gchar *tmp; entlist = g_list_first(entlist); while (entlist) { fldlist = g_list_first(entlist->data); while (fldlist) { attlist = g_list_first(fldlist->data); while (attlist) { tmp = (gchar *)attlist->data; TRACE(TRACE_DEBUG,"value [%s]", tmp); values = g_list_append_printf(values,"%s", tmp); if (! g_list_next(attlist)) break; attlist = g_list_next(attlist); } if (! g_list_next(fldlist)) break; fldlist = g_list_next(fldlist); } if (! g_list_next(entlist)) break; entlist = g_list_next(entlist); } return values; } static char *dm_ldap_get_filter(const gchar boolean, const gchar *attribute, GList *values) { /* build user filter from objectclasses */ gchar *s; GString *t = g_string_new(""); GString *q = g_string_new(""); GList *l = NULL; values = g_list_first(values); while (values) { g_string_printf(t,"%s=%s", attribute, (char *)values->data); l = g_list_append(l,g_strdup(t->str)); if (! g_list_next(values)) break; values = g_list_next(values); } t = g_list_join(l,")("); g_string_printf(q,"(%c(%s))", boolean, t->str); s = q->str; g_string_free(t,TRUE); g_string_free(q,FALSE); g_list_foreach(l,(GFunc)g_free,NULL); g_list_free(l); return s; } static u64_t dm_ldap_get_freeid(const gchar *attribute) { /* get the first available uidNumber/gidNumber */ u64_t id = 0, t; GList *ids, *entlist; u64_t min = 0, max = 0; char *attrs[2] = { (char *)attribute, NULL }; GString *q = g_string_new(""); u64_t *key; g_string_printf(q,"(%s=*)", attribute); entlist = __auth_get_every_match(q->str, attrs); ids = dm_ldap_entdm_list_get_values(entlist); /* get the valid range */ if (strcmp(attribute,_ldap_cfg.field_nid)==0) { min = strtoull(_ldap_cfg.min_nid,NULL,10); max = strtoull(_ldap_cfg.max_nid,NULL,10); } if (strcmp(attribute,_ldap_cfg.field_cid)==0) { min = strtoull(_ldap_cfg.min_cid,NULL,10); max = strtoull(_ldap_cfg.max_cid,NULL,10); } g_assert(min < max); /* allocate the key array */ key = g_new0(u64_t, 1 + max - min ); /* get all used ids */ ids = g_list_first(ids); while (ids) { t = strtoull(ids->data,NULL,10); if ( (t >= min) && (t <= max) ) key[t-min] = t; if (! g_list_next(ids)) break; ids = g_list_next(ids); } /* find the first unused id */ for (t = min; t <= max; t++) { if (! (key[t-min])) break; } g_assert( (t >= min) && (t <= max) ); /* cleanup */ g_free(key); g_list_foreach(ids,(GFunc)g_free,NULL); g_list_free(ids); id=t; TRACE(TRACE_DEBUG,"return free id [%llu]\n", id); return id; } static char * dm_ldap_user_getdn(u64_t user_idnr) { GString *t = g_string_new(""); char *dn; g_string_printf(t, "(%s=%llu)", _ldap_cfg.field_nid, user_idnr); TRACE(TRACE_DEBUG, "searching with query [%s]", t->str); if (auth_search(t->str)) { g_string_free(t,TRUE); return NULL; } if (ldap_count_entries(_ldap_conn, _ldap_res) < 1) { TRACE(TRACE_DEBUG, "no entries found"); g_string_free(t,TRUE); ldap_msgfree(_ldap_res); return NULL; } if (! (_ldap_msg = ldap_first_entry(_ldap_conn, _ldap_res))) { ldap_get_option(_ldap_conn, LDAP_OPT_ERROR_NUMBER, &_ldap_err); TRACE(TRACE_ERROR, "ldap_first_entry failed: %s", ldap_err2string(_ldap_err)); ldap_msgfree(_ldap_res); return NULL; } if (! (dn = ldap_get_dn(_ldap_conn, _ldap_msg))) { ldap_get_option(_ldap_conn, LDAP_OPT_ERROR_NUMBER, &_ldap_err); TRACE(TRACE_ERROR, "ldap_get_dn failed: %s", ldap_err2string(_ldap_err)); ldap_msgfree(_ldap_res); return NULL; } ldap_msgfree(_ldap_res); return dn; } static int dm_ldap_mod_field(u64_t user_idnr, const char *fieldname, const char *newvalue) { LDAPMod *mods[2], modField; char *newvalues[2]; if (! user_idnr) { TRACE(TRACE_ERROR, "no user_idnr specified"); return -1; } if (! fieldname) { TRACE(TRACE_ERROR, "no fieldname specified"); return -1; } if (! newvalue) { TRACE(TRACE_ERROR, "no new value specified"); return -1; } if (! (_ldap_dn = dm_ldap_user_getdn(user_idnr))) return -1; newvalues[0] = (char *)newvalue; newvalues[1] = NULL; modField.mod_op = LDAP_MOD_REPLACE; modField.mod_type = (char *)fieldname; modField.mod_values = newvalues; mods[0] = &modField; mods[1] = NULL; _ldap_err = ldap_modify_s(_ldap_conn, _ldap_dn, mods); if (_ldap_err) { TRACE(TRACE_ERROR,"dn: %s, %s: %s [%s]", _ldap_dn, fieldname, newvalue, ldap_err2string(_ldap_err)); ldap_memfree(_ldap_dn); return -1; } TRACE(TRACE_DEBUG,"dn: %s, %s: %s", _ldap_dn, fieldname, newvalue); ldap_memfree(_ldap_dn); return 0; } /* OLD-SCHOOL: * * Each node of retlist contains a data field * which is a pointer to another list, "fieldlist". * * Each node of fieldlist contains a data field * which is a pointer to another list, "datalist". * * Each node of datalist contains a data field * which is a (char *) pointer to some actual data. * * Here's a visualization: * * retlist * has the "rows" that matched * { * (struct dm_list *)data * has the fields you requested * { * (struct dm_list *)data * has the values for the field * { * (char *)data * (char *)data * (char *)data * } * } * } * * TODO: GLIB-STYLIE: * * ghashtable *ldap_entities * { * gchar *dn; * ghashtable *ldap_attributes { * gchar *attribute; * glist *values; * } * } * */ /* returns the number of matches found */ static GList * __auth_get_every_match(const char *q, char **retfields) { LDAPMessage *ldap_msg; char **ldap_vals = NULL; char *dn; int j = 0, k = 0, m = 0; GList *attlist,*fldlist,*entlist; attlist = fldlist = entlist = NULL; if (auth_search(q)) return NULL; if ((j = ldap_count_entries(_ldap_conn, _ldap_res)) < 1) { TRACE(TRACE_DEBUG, "nothing found"); if (_ldap_res) ldap_msgfree(_ldap_res); return NULL; } /* do the first entry here */ if ((ldap_msg = ldap_first_entry(_ldap_conn, _ldap_res)) == NULL) { ldap_get_option(_ldap_conn, LDAP_OPT_ERROR_NUMBER, &_ldap_err); TRACE(TRACE_ERROR, "ldap_first_entry failed: [%s]", ldap_err2string(_ldap_err)); if (_ldap_res) ldap_msgfree(_ldap_res); return NULL; } while (ldap_msg) { dn = ldap_get_dn(_ldap_conn, ldap_msg); TRACE(TRACE_DEBUG,"scan results for DN: [%s]", dn); for (k = 0; retfields[k] != NULL; k++) { TRACE(TRACE_DEBUG,"ldap_get_values [%s]", retfields[k]); if ((ldap_vals = ldap_get_values(_ldap_conn, ldap_msg, retfields[k]))) { m = 0; while (ldap_vals[m]) { TRACE(TRACE_DEBUG,"got value [%s]", ldap_vals[m]); attlist = g_list_append(attlist,g_strdup(ldap_vals[m])); m++; } } fldlist = g_list_append(fldlist, attlist); attlist = NULL; ldap_value_free(ldap_vals); } entlist = g_list_append(entlist, fldlist); fldlist = NULL; ldap_memfree(dn); ldap_msg = ldap_next_entry(_ldap_conn, ldap_msg); } if (_ldap_res) ldap_msgfree(_ldap_res); if (ldap_msg) ldap_msgfree(ldap_msg); return entlist; } static char *__auth_get_first_match(const char *q, char **retfields) { LDAPMessage *ldap_msg; char *returnid = NULL; char *ldap_dn = NULL; char **ldap_vals = NULL; int k = 0; if (auth_search(q)) return NULL; if (ldap_count_entries(_ldap_conn, _ldap_res) < 1) { TRACE(TRACE_DEBUG, "none found"); goto endfree; } ldap_msg = ldap_first_entry(_ldap_conn, _ldap_res); if (ldap_msg == NULL) { ldap_get_option(_ldap_conn, LDAP_OPT_ERROR_NUMBER, &_ldap_err); TRACE(TRACE_ERROR, "ldap_first_entry failed: %s", ldap_err2string(_ldap_err)); goto endfree; } for (k = 0; retfields[k] != NULL; k++) { if (0 == strcasecmp(retfields[k], "dn")) { ldap_dn = ldap_get_dn(_ldap_conn, ldap_msg); if (ldap_dn) returnid = g_strdup(ldap_dn); break; } else { ldap_vals = ldap_get_values(_ldap_conn, ldap_msg, retfields[k]); if (ldap_vals) returnid = g_strdup(ldap_vals[0]); break; } } endfree: if (ldap_dn) ldap_memfree(ldap_dn); if (ldap_vals) ldap_value_free(ldap_vals); if (_ldap_res) ldap_msgfree(_ldap_res); TRACE(TRACE_DEBUG,"returnid [%s]",returnid); return returnid; } int auth_user_exists(const char *username, u64_t * user_idnr) { char *id_char; char query[AUTH_QUERY_SIZE]; char *fields[] = { _ldap_cfg.field_nid, NULL }; assert(user_idnr != NULL); *user_idnr = 0; if (!username) { TRACE(TRACE_ERROR, "got NULL as username"); return 0; } /* fall back to db-user for DBMAIL_DELIVERY_USERNAME */ if (strcmp(username,DBMAIL_DELIVERY_USERNAME)==0) return db_user_exists(DBMAIL_DELIVERY_USERNAME, user_idnr); snprintf(query, AUTH_QUERY_SIZE, "(%s=%s)", _ldap_cfg.field_uid, username); id_char = __auth_get_first_match(query, fields); *user_idnr = (id_char) ? strtoull(id_char, NULL, 0) : 0; if (id_char != NULL) g_free(id_char); TRACE(TRACE_DEBUG, "returned value is [%llu]", *user_idnr); if (*user_idnr != 0) return 1; return 0; } /* Given a useridnr, find the account/login name * return 0 if not found, NULL on error */ char *auth_get_userid(u64_t user_idnr) { char *returnid = NULL; char query[AUTH_QUERY_SIZE]; char *fields[] = { _ldap_cfg.field_uid, NULL }; snprintf(query, AUTH_QUERY_SIZE, "(%s=%llu)", _ldap_cfg.field_nid, user_idnr); returnid = __auth_get_first_match(query, fields); TRACE(TRACE_DEBUG, "returned value is [%s]", returnid); return returnid; } /* We'd like to have -1 return on failure, but * the internal ldap api here won't tell us. */ int auth_check_userid(u64_t user_idnr) { char *returnid = NULL; char query[AUTH_QUERY_SIZE]; char *fields[] = { _ldap_cfg.field_nid, NULL }; int ret; snprintf(query, AUTH_QUERY_SIZE, "(%s=%llu)", _ldap_cfg.field_nid, user_idnr); returnid = __auth_get_first_match(query, fields); if (returnid) { ret = 0; TRACE(TRACE_DEBUG, "found user_idnr [%llu]", user_idnr); } else { ret = 1; TRACE(TRACE_DEBUG, "didn't find user_idnr [%llu]", user_idnr); } g_free(returnid); return ret; } /* * Get the Client ID number * Return 0 on successful failure * Return -1 on really big failures */ int auth_getclientid(u64_t user_idnr, u64_t * client_idnr) { char *cid_char = NULL; char query[AUTH_QUERY_SIZE]; char *fields[] = { _ldap_cfg.field_cid, NULL }; assert(client_idnr != NULL); *client_idnr = 0; if (!user_idnr) { TRACE(TRACE_ERROR, "got NULL as useridnr"); return -1; } snprintf(query, AUTH_QUERY_SIZE, "(%s=%llu)", _ldap_cfg.field_nid, user_idnr); cid_char = __auth_get_first_match(query, fields); *client_idnr = (cid_char) ? strtoull(cid_char, NULL, 0) : 0; if (cid_char != NULL) g_free(cid_char); TRACE(TRACE_DEBUG, "found client_idnr [%llu]", *client_idnr); return 1; } int auth_getmaxmailsize(u64_t user_idnr, u64_t * maxmail_size) { char *max_char; char query[AUTH_QUERY_SIZE]; char *fields[] = { _ldap_cfg.field_maxmail, NULL }; assert(maxmail_size != NULL); *maxmail_size = 0; if (!user_idnr) { TRACE(TRACE_ERROR, "got NULL as useridnr"); return 0; } snprintf(query, AUTH_QUERY_SIZE, "(%s=%llu)", _ldap_cfg.field_nid, user_idnr); max_char = __auth_get_first_match(query, fields); *maxmail_size = (max_char) ? strtoull(max_char, 0, 10) : 0; // if max_char is NULL g_free will not fail it simply return g_free(max_char); TRACE(TRACE_DEBUG, "returned value is [%llu]", *maxmail_size); return 1; } /* * auth_getencryption() * * returns a string describing the encryption used for the passwd storage * for this user. * The string is valid until the next function call; in absence of any * encryption the string will be empty (not null). * * If the specified user does not exist an empty string will be returned. */ char *auth_getencryption(u64_t user_idnr UNUSED) { /* ldap does not support fancy passwords, but return * something valid for the sql shadow */ return "md5"; } /* Fills the users list with all existing users * return -2 on mem error, -1 on db-error, 0 on success */ GList * auth_get_known_users(void) { char *query; char *fields[] = { _ldap_cfg.field_uid, NULL }; GList *users; GList *entlist; GString *t = g_string_new(_ldap_cfg.user_objectclass); GList *l = g_string_split(t,","); g_string_free(t,TRUE); query = dm_ldap_get_filter('&',"objectClass",l); entlist = __auth_get_every_match(query, fields); g_free(query); TRACE(TRACE_INFO, "found %d users", g_list_length(entlist)); users = dm_ldap_entdm_list_get_values(entlist); dm_ldap_freeresult(entlist); return users; } /* Fills the aliases list with all existing aliases * return -2 on mem error, -1 on db-error, 0 on succeses */ GList * auth_get_known_aliases(void) { char *query; char *fields[] = { _ldap_cfg.field_uid, NULL }; GList *aliases; GList *entlist; GString *t = g_string_new(_ldap_cfg.forw_objectclass); GList *l = g_string_split(t,","); g_string_free(t,TRUE); query = dm_ldap_get_filter('&',"objectClases",l); entlist = __auth_get_every_match(query, fields); g_free(query); TRACE(TRACE_INFO, "found %d aliases", g_list_length(entlist)); aliases = dm_ldap_entdm_list_get_values(entlist); dm_ldap_freeresult(entlist); return aliases; } /* * auth_check_user_ext() * * As auth_check_user() but adds the numeric ID of the user found * to userids or the forward to the fwds. * * returns the number of occurences. */ int auth_check_user_ext(const char *address, struct dm_list *userids, struct dm_list *fwds, int checks) { int occurences = 0; u64_t id; char *endptr = NULL; char query[AUTH_QUERY_SIZE]; char *fields[] = { _ldap_cfg.field_nid, _ldap_cfg.field_fwdtarget[0] ? _ldap_cfg.field_fwdtarget : NULL, NULL }; char *attrvalue; GList *entlist, *fldlist, *attlist; if (checks > 20) { TRACE(TRACE_ERROR, "too many checks. Possible loop detected."); return 0; } TRACE(TRACE_DEBUG, "checking user [%s] in alias table", address); /* This is my private line for sending a DN rather than a search */ snprintf(query, AUTH_QUERY_SIZE, "(%s=%s)", _ldap_cfg.field_mail, address); TRACE(TRACE_DEBUG, "searching with query [%s], checks [%d]", query, checks); entlist = __auth_get_every_match(query, fields); if (g_list_length(entlist) < 1) { if (checks > 0) { /* found the last one, this is the deliver to * but checks needs to be bigger then 0 because * else it could be the first query failure */ id = strtoull(address, &endptr, 10); if (*endptr == 0) { /* numeric deliver-to --> this is a userid */ TRACE(TRACE_DEBUG, "adding [%llu] to userids", id); dm_list_nodeadd(userids, &id, sizeof(id)); } else { TRACE(TRACE_DEBUG, "adding [%s] to forwards", address); dm_list_nodeadd(fwds, address, strlen(address) + 1); } return 1; } else { TRACE(TRACE_DEBUG, "user [%s] not in aliases table", address); dm_ldap_freeresult(entlist); return 0; } } TRACE(TRACE_DEBUG, "into checking loop"); entlist = g_list_first(entlist); while (entlist) { fldlist = g_list_first(entlist->data); while(fldlist) { attlist = g_list_first(fldlist->data); while(attlist) { attrvalue = (char *)attlist->data; occurences += auth_check_user_ext(attrvalue, userids, fwds, checks+1); if (! g_list_next(attlist)) break; attlist = g_list_next(attlist); } if (! g_list_next(fldlist)) break; fldlist = g_list_next(fldlist); } if (! g_list_next(entlist)) break; entlist = g_list_next(entlist); } dm_ldap_freeresult(entlist); return occurences; } /* * auth_adduser() * * adds a new user to the database * and adds a INBOX.. * returns a 1 on succes, -1 on failure */ int auth_adduser(const char *username, const char *password, const char *enctype UNUSED, u64_t clientid, u64_t maxmail, u64_t * user_idnr) { int i = 0, result; GString *nid = g_string_new(""); GString *cid = g_string_new(""); GString *maxm = g_string_new(""); u64_t newidnr = dm_ldap_get_freeid(_ldap_cfg.field_nid); g_string_printf(nid,"%llu", newidnr); g_string_printf(cid,"%llu",clientid); g_string_printf(maxm,"%llu",maxmail); char **obj_values = g_strsplit(_ldap_cfg.user_objectclass,",",0); char *pw_values[] = { (char *)password, NULL }; char *uid_values[] = { (char *)username, NULL }; char *nid_values[] = { nid->str, NULL }; char *cid_values[] = { cid->str, NULL }; char *max_values[] = { maxm->str, NULL }; field_t mail_type = "mail"; field_t obj_type = "objectClass"; GString *t=g_string_new(""); assert(user_idnr != NULL); *user_idnr = 0; g_string_printf(t,"%s=%s,%s", _ldap_cfg.cn_string, username, _ldap_cfg.base_dn); _ldap_dn=g_strdup(t->str); g_string_free(t,TRUE); TRACE(TRACE_DEBUG, "Adding user with DN of [%s]", _ldap_dn); LDAPMod *mods[10], mod_obj_type, mod_field_passwd, mod_mail_type, mod_field_uid, mod_field_cid, mod_field_maxmail, mod_field_nid; mod_obj_type.mod_op = LDAP_MOD_ADD; mod_obj_type.mod_type = obj_type; mod_obj_type.mod_values = obj_values; mods[i++] = &mod_obj_type; if (strlen(_ldap_cfg.field_passwd) > 0) { mod_field_passwd.mod_op = LDAP_MOD_ADD; mod_field_passwd.mod_type = _ldap_cfg.field_passwd; mod_field_passwd.mod_values = pw_values; mods[i++] = &mod_field_passwd; } mod_mail_type.mod_op = LDAP_MOD_ADD; mod_mail_type.mod_type = mail_type; mod_mail_type.mod_values = uid_values; mods[i++] = &mod_mail_type; mod_field_uid.mod_op = LDAP_MOD_ADD; mod_field_uid.mod_type = _ldap_cfg.field_uid; mod_field_uid.mod_values = uid_values; mods[i++] = &mod_field_uid; mod_field_cid.mod_op = LDAP_MOD_ADD; mod_field_cid.mod_type = _ldap_cfg.field_cid; mod_field_cid.mod_values = cid_values; mods[i++] = &mod_field_cid; mod_field_maxmail.mod_op = LDAP_MOD_ADD; mod_field_maxmail.mod_type = _ldap_cfg.field_maxmail; mod_field_maxmail.mod_values = max_values; mods[i++] = &mod_field_maxmail; mod_field_nid.mod_op = LDAP_MOD_ADD; mod_field_nid.mod_type = _ldap_cfg.field_nid; mod_field_nid.mod_values = nid_values; mods[i++] = &mod_field_nid; mods[i++] = NULL; _ldap_err = ldap_add_s(_ldap_conn, _ldap_dn, mods); g_strfreev(obj_values); ldap_memfree(_ldap_dn); if (_ldap_err) { TRACE(TRACE_ERROR, "could not add user: %s", ldap_err2string(_ldap_err)); return -1; } *user_idnr = newidnr; result = db_user_create_shadow(username, user_idnr); if (result != 1) { TRACE(TRACE_ERROR, "sql shadow account creation failed"); auth_delete_user(username); *user_idnr=0; return result; } return 1; } int auth_delete_user(const char *username) { /* look up who's got that username, get their dn, and delete it! */ if (!username) { TRACE(TRACE_ERROR, "got NULL as useridnr"); return 0; } snprintf(_ldap_query, AUTH_QUERY_SIZE, "(%s=%s)", _ldap_cfg.field_uid, username); if (auth_search(_ldap_query)) return -1; if (ldap_count_entries(_ldap_conn, _ldap_res) < 1) { TRACE(TRACE_DEBUG, "no entries found"); ldap_msgfree(_ldap_res); return 0; } _ldap_msg = ldap_first_entry(_ldap_conn, _ldap_res); if (_ldap_msg == NULL) { ldap_get_option(_ldap_conn, LDAP_OPT_ERROR_NUMBER, &_ldap_err); TRACE(TRACE_ERROR, "ldap_first_entry failed: %s", ldap_err2string(_ldap_err)); ldap_msgfree(_ldap_res); return -1; } _ldap_dn = ldap_get_dn(_ldap_conn, _ldap_msg); if (_ldap_dn) { TRACE(TRACE_DEBUG, "deleting user at dn [%s]", _ldap_dn); _ldap_err = ldap_delete_s(_ldap_conn, _ldap_dn); if (_ldap_err) { TRACE(TRACE_ERROR, "could not delete dn: %s", ldap_err2string(_ldap_err)); ldap_memfree(_ldap_dn); ldap_msgfree(_ldap_res); return -1; } } ldap_memfree(_ldap_dn); ldap_msgfree(_ldap_res); if (db_user_delete(username)) { TRACE(TRACE_ERROR, "sql shadow account deletion failed"); } return 0; } static int dm_ldap_user_shadow_rename(u64_t user_idnr, const char *new_name) { char *oldname; u64_t dbidnr; oldname = auth_get_userid(user_idnr); db_user_exists(oldname,&dbidnr); if (dbidnr) { TRACE(TRACE_DEBUG, "call db_user_rename ([%llu],[%s])\n", dbidnr, new_name); } if ((! dbidnr) || (db_user_rename(dbidnr, new_name))) { TRACE(TRACE_ERROR, "renaming shadow account in db failed for [%llu]->[%s]", user_idnr, new_name); return -1; } return 0; } int auth_change_username(u64_t user_idnr, const char *new_name) { GString *newrdn; if (!user_idnr) { TRACE(TRACE_ERROR, "got NULL as useridnr"); return -1; } if (!new_name) { TRACE(TRACE_ERROR, "got NULL as new_name"); return -1; } if (! (_ldap_dn = dm_ldap_user_getdn(user_idnr))) return -1; TRACE(TRACE_DEBUG, "got DN [%s]", _ldap_dn); db_begin_transaction(); dm_ldap_user_shadow_rename(user_idnr, new_name); /* perhaps we have to rename the dn */ if (strcmp(_ldap_cfg.field_uid,_ldap_cfg.cn_string)==0) { newrdn = g_string_new(""); g_string_printf(newrdn,"%s=%s", _ldap_cfg.cn_string,new_name); _ldap_err = ldap_modrdn_s(_ldap_conn, _ldap_dn, newrdn->str); ldap_memfree(_ldap_dn); g_string_free(newrdn,TRUE); if (_ldap_err) { TRACE(TRACE_ERROR, "error calling ldap_modrdn_s [%s]", ldap_err2string(_ldap_err)); db_rollback_transaction(); return -1; } db_commit_transaction(); return 0; } /* else we need to modify an attribute */ ldap_memfree(_ldap_dn); if (dm_ldap_mod_field(user_idnr, _ldap_cfg.field_uid, new_name)) { db_rollback_transaction(); return -1; } db_commit_transaction(); return 0; } int auth_change_password(u64_t user_idnr, const char *new_pass, const char *enctype UNUSED) { return dm_ldap_mod_field(user_idnr, _ldap_cfg.field_passwd, new_pass); } int auth_change_clientid(u64_t user_idnr, u64_t newcid) { char newcid_str[16]; snprintf(newcid_str, 16, "%llu", newcid); return dm_ldap_mod_field(user_idnr, _ldap_cfg.field_cid, newcid_str); } int auth_change_mailboxsize(u64_t user_idnr, u64_t new_size) { int result; char newsize_str[16]; snprintf(newsize_str, 16, "%llu", new_size); if ((result = db_change_mailboxsize(user_idnr, new_size))) return result; return dm_ldap_mod_field(user_idnr, _ldap_cfg.field_maxmail, newsize_str); } /* * auth_validate() * * tries to validate user 'user' * * returns useridnr on OK, 0 on validation failed, -1 on error */ int auth_validate(clientinfo_t *ci, char *username, char *password, u64_t * user_idnr) { timestring_t timestring; char real_username[DM_USERNAME_LEN]; int result; u64_t mailbox_idnr; int ldap_err; char *ldap_dn = NULL; assert(user_idnr != NULL); *user_idnr = 0; if (username == NULL || password == NULL) { TRACE(TRACE_DEBUG, "username or password is NULL"); return 0; } /* the shared mailbox user should not log in! */ if (strcmp(username, PUBLIC_FOLDER_USER) == 0) return 0; memset(real_username,'\0', sizeof(real_username)); create_current_timestring(×tring); strncpy(real_username, username, DM_USERNAME_LEN); if (db_use_usermap()) { /* use usermap */ result = db_usermap_resolve(ci, username, real_username); if (result == DM_EGENERAL) return 0; if (result == DM_EQUERY) return DM_EQUERY; } if (auth_user_exists(real_username, user_idnr) != 1) { return 0; } if (! (ldap_dn = dm_ldap_user_getdn(*user_idnr))) { TRACE(TRACE_ERROR,"unable to determine DN for user"); return 0; } /* now, try to rebind as the given DN using the supplied password */ TRACE(TRACE_DEBUG, "rebinding as [%s] to validate password", ldap_dn); ldap_err = ldap_bind_s(_ldap_conn, ldap_dn, password, LDAP_AUTH_SIMPLE); if (ldap_err) { TRACE(TRACE_ERROR, "ldap_bind_s failed: %s", ldap_err2string(ldap_err)); *user_idnr = 0; } else { db_user_log_login(*user_idnr); } /* rebind as admin */ auth_ldap_bind(); if (ldap_dn) ldap_memfree(ldap_dn); if (*user_idnr == 0) return 0; db_find_create_mailbox("INBOX", BOX_DEFAULT, *user_idnr, &mailbox_idnr); return 1; } /* returns useridnr on OK, 0 on validation failed, -1 on error */ u64_t auth_md5_validate(clientinfo_t *ci UNUSED, char *username UNUSED, unsigned char *md5_apop_he UNUSED, char *apop_stamp UNUSED) { return -1; } /** * \brief get user ids belonging to a client id * \param client_id * \param user_ids * \param num_users * \return * - -2 on memory error * - -1 on database error * - 1 on success */ int auth_get_users_from_clientid(u64_t client_id UNUSED, /*@out@*/ u64_t ** user_ids UNUSED, /*@out@*/ unsigned *num_users UNUSED) { return 1; } /** * \brief get a list of aliases associated with a user's user_idnr * \param user_idnr idnr of user * \param aliases list of aliases * \return * - -2 on memory failure * - -1 on database failure * - 0 on success * \attention aliases list needs to be empty. Method calls dm_list_init() * which sets list->start to NULL. */ GList * auth_get_user_aliases(u64_t user_idnr) { char *fields[] = { _ldap_cfg.field_mail, NULL }; GString *t = g_string_new(""); GList *aliases = NULL; GList *entlist, *fldlist, *attlist; g_string_printf(t,"%s=%llu", _ldap_cfg.field_nid, user_idnr); if ((entlist = __auth_get_every_match(t->str, fields))) { entlist = g_list_first(entlist); fldlist = g_list_first(entlist->data); attlist = g_list_first(fldlist->data); while (attlist) { aliases = g_list_append(aliases, g_strdup(attlist->data)); if (! g_list_next(attlist)) break; attlist = g_list_next(attlist); } dm_ldap_freeresult(entlist); } g_string_free(t,TRUE); return aliases; } /** * \brief get a list of aliases associated with a user's user_idnr * \param user_idnr idnr of user * \param aliases list of aliases * \return * - -2 on memory failure * - -1 on database failure * - 0 on success * \attention aliases list needs to be empty. Method calls dm_list_init() * which sets list->start to NULL. */ GList * auth_get_aliases_ext(const char *alias) { char *fields[] = { _ldap_cfg.field_fwdtarget, NULL }; GString *t = g_string_new(""); GList *aliases = NULL; GList *entlist, *fldlist, *attlist; g_string_printf(t,"%s=%s", _ldap_cfg.field_mail, alias); if ((entlist = __auth_get_every_match(t->str, fields))) { entlist = g_list_first(entlist); fldlist = g_list_first(entlist->data); attlist = g_list_first(fldlist->data); while (attlist) { aliases = g_list_append(aliases, g_strdup(attlist->data)); if (! g_list_next(attlist)) break; attlist = g_list_next(attlist); } dm_ldap_freeresult(entlist); } g_string_free(t,TRUE); return aliases; } /** * \brief add an alias for a user * \param user_idnr user's id * \param alias new alias * \param clientid client id * \return * - -1 on failure * - 0 on success * - 1 if alias already exists for given user */ int auth_addalias(u64_t user_idnr, const char *alias, u64_t clientid UNUSED) { char *userid = NULL; char **mailValues = NULL; LDAPMod *modify[2], addMail; GList *aliases; if (! (userid = auth_get_userid(user_idnr))) return -1; /* check the alias newval against the known aliases for this user */ aliases = auth_get_user_aliases(user_idnr); aliases = g_list_first(aliases); while (aliases) { if (strcmp(alias,(char *)aliases->data)==0) { g_list_foreach(aliases,(GFunc)g_free,NULL); g_list_free(aliases); return 1; } if (! g_list_next(aliases)) break; aliases = g_list_next(aliases); } g_list_foreach(aliases,(GFunc)g_free,NULL); g_list_free(aliases); /* get the DN for this user */ if (! (_ldap_dn = dm_ldap_user_getdn(user_idnr))) return -1; /* construct and apply the changes */ mailValues = g_strsplit(alias,",",1); addMail.mod_op = LDAP_MOD_ADD; addMail.mod_type = _ldap_cfg.field_mail; addMail.mod_values = mailValues; modify[0] = &addMail; modify[1] = NULL; _ldap_err = ldap_modify_s(_ldap_conn, _ldap_dn, modify); g_strfreev(mailValues); ldap_memfree(_ldap_dn); if (_ldap_err) { TRACE(TRACE_ERROR, "update failed: %s", ldap_err2string(_ldap_err)); return -1; } return 0; } /** * \brief add an alias to deliver to an extern address * \param alias the alias * \param deliver_to extern address to deliver to * \param clientid client idnr * \return * - -1 on failure * - 0 on success * - 1 if deliver_to already exists for given alias */ /* find forwarding alias * \return * - -1 error * - 0 success * - 1 dn exists but no such deliver_to */ static int forward_exists(const char *alias, const char *deliver_to) { char *objectfilter, *dn; char *fields[] = { "dn", _ldap_cfg.field_fwdtarget, NULL }; int result = 0; GString *t = g_string_new(_ldap_cfg.forw_objectclass); GList *l = g_string_split(t,","); objectfilter = dm_ldap_get_filter('&',"objectClass", l); g_string_printf(t,"(&%s(%s=%s)(%s=%s))", objectfilter, _ldap_cfg.cn_string, alias, _ldap_cfg.field_fwdtarget, deliver_to); dn = __auth_get_first_match(t->str, fields); if (! dn) { result = -1; // assume total failure; g_string_printf(t,"(&%s(%s=%s))", objectfilter, _ldap_cfg.cn_string, alias); dn = __auth_get_first_match(t->str, fields); if (dn) result = 1; // dn does exist, just this forward is missing } g_free(objectfilter); g_free(dn); g_string_free(t,TRUE); g_list_foreach(l,(GFunc)g_free,NULL); TRACE(TRACE_DEBUG, "result [%d]", result); return result; } static int forward_create(const char *alias, const char *deliver_to) { LDAPMod *mods[5], objectClass, cnField, mailField, forwField; char **obj_values = g_strsplit(_ldap_cfg.forw_objectclass,",",0); char *cn_values[] = { (char *)alias, NULL }; char *mail_values[] = { (char *)alias, NULL }; char *forw_values[] = { (char *)deliver_to, NULL }; GString *t=g_string_new(""); g_string_printf(t,"%s=%s,%s", _ldap_cfg.cn_string, alias, _ldap_cfg.base_dn); _ldap_dn=g_strdup(t->str); g_string_free(t,TRUE); TRACE(TRACE_DEBUG, "Adding forwardingAddress with DN of [%s]", _ldap_dn); objectClass.mod_op = LDAP_MOD_ADD; objectClass.mod_type = "objectClass"; objectClass.mod_values = obj_values; cnField.mod_op = LDAP_MOD_ADD; cnField.mod_type = _ldap_cfg.cn_string; cnField.mod_values = cn_values; mailField.mod_op = LDAP_MOD_ADD; mailField.mod_type = _ldap_cfg.field_mail; mailField.mod_values = mail_values; forwField.mod_op = LDAP_MOD_ADD; forwField.mod_type = _ldap_cfg.field_fwdtarget; forwField.mod_values = forw_values; mods[0] = &objectClass; mods[1] = &cnField; mods[2] = &mailField; mods[3] = &forwField; mods[4] = NULL; TRACE(TRACE_DEBUG, "creating new forward [%s] -> [%s]", alias, deliver_to); _ldap_err = ldap_add_s(_ldap_conn, _ldap_dn, mods); g_strfreev(obj_values); ldap_memfree(_ldap_dn); if (_ldap_err) { TRACE(TRACE_ERROR, "could not add forwardingAddress: %s", ldap_err2string(_ldap_err)); return -1; } return 0; } static int forward_add(const char *alias,const char *deliver_to) { char **mailValues = NULL; LDAPMod *modify[2], addForw; GString *t=g_string_new(""); g_string_printf(t,"%s=%s,%s", _ldap_cfg.cn_string, alias, _ldap_cfg.base_dn); _ldap_dn=g_strdup(t->str); g_string_free(t,TRUE); /* construct and apply the changes */ mailValues = g_strsplit(deliver_to,",",1); addForw.mod_op = LDAP_MOD_ADD; addForw.mod_type = _ldap_cfg.field_fwdtarget; addForw.mod_values = mailValues; modify[0] = &addForw; modify[1] = NULL; TRACE(TRACE_DEBUG, "creating additional forward [%s] -> [%s]", alias, deliver_to); _ldap_err = ldap_modify_s(_ldap_conn, _ldap_dn, modify); g_strfreev(mailValues); ldap_memfree(_ldap_dn); if (_ldap_err) { TRACE(TRACE_ERROR, "update failed: %s", ldap_err2string(_ldap_err)); return -1; } return 0; } static int forward_delete(const char *alias, const char *deliver_to) { char **mailValues = NULL; LDAPMod *modify[2], delForw; GString *t=g_string_new(""); g_string_printf(t,"%s=%s,%s", _ldap_cfg.cn_string, alias, _ldap_cfg.base_dn); _ldap_dn=g_strdup(t->str); g_string_free(t,TRUE); /* construct and apply the changes */ mailValues = g_strsplit(deliver_to,",",1); delForw.mod_op = LDAP_MOD_DELETE; delForw.mod_type = _ldap_cfg.field_fwdtarget; delForw.mod_values = mailValues; modify[0] = &delForw; modify[1] = NULL; TRACE(TRACE_DEBUG, "delete additional forward [%s] -> [%s]", alias, deliver_to); _ldap_err = ldap_modify_s(_ldap_conn, _ldap_dn, modify); g_strfreev(mailValues); if (_ldap_err) { TRACE(TRACE_DEBUG, "delete additional forward failed, removing dn [%s]", _ldap_dn); _ldap_err = ldap_delete_s(_ldap_conn, _ldap_dn); if (_ldap_err) TRACE(TRACE_ERROR, "deletion failed [%s]", ldap_err2string(_ldap_err)); } ldap_memfree(_ldap_dn); return 0; } int auth_addalias_ext(const char *alias, const char *deliver_to, u64_t clientid UNUSED) { switch(forward_exists(alias,deliver_to)) { case -1: return forward_create(alias,deliver_to); case 1: return forward_add(alias,deliver_to); } return 0; } /** * \brief remove alias for user * \param user_idnr user id * \param alias the alias * \return * - -1 on failure * - 0 on success */ int auth_removealias(u64_t user_idnr, const char *alias) { char *userid = NULL; char **mailValues = NULL; LDAPMod *modify[2], delMail; GList *aliases; if (! (userid = auth_get_userid(user_idnr))) return -1; /* check the alias against the known aliases for this user */ aliases = auth_get_user_aliases(user_idnr); aliases = g_list_first(aliases); while (aliases) { if (strcmp(alias,(char *)aliases->data)==0) break; if (! g_list_next(aliases)) break; aliases = g_list_next(aliases); } if (!aliases) { TRACE(TRACE_DEBUG,"alias [%s] for user [%s] not found", alias, userid); g_list_foreach(aliases,(GFunc)g_free,NULL); g_list_free(aliases); return 1; } g_list_foreach(aliases,(GFunc)g_free,NULL); g_list_free(aliases); /* get the DN for this user */ if (! (_ldap_dn = dm_ldap_user_getdn(user_idnr))) return -1; /* construct and apply the changes */ mailValues = g_strsplit(alias,",",1); delMail.mod_op = LDAP_MOD_DELETE; delMail.mod_type = _ldap_cfg.field_mail; delMail.mod_values = mailValues; modify[0] = &delMail; modify[1] = NULL; _ldap_err = ldap_modify_s(_ldap_conn, _ldap_dn, modify); if (_ldap_err) { TRACE(TRACE_ERROR, "update failed: %s", ldap_err2string(_ldap_err)); g_strfreev(mailValues); ldap_memfree(_ldap_dn); return -1; } g_strfreev(mailValues); ldap_memfree(_ldap_dn); return 0; } /** * \brief remove external delivery address for an alias * \param alias the alias * \param deliver_to the deliver to address the alias is * pointing to now * \return * - -1 on failure * - 0 on success */ int auth_removealias_ext(const char *alias, const char *deliver_to) { int check; check = forward_exists(alias,deliver_to); if (check) return 0; return forward_delete(alias,deliver_to); } gboolean auth_requires_shadow_user(void) { return TRUE; }