X-Git-Url: https://git.hcoop.net/hcoop/debian/courier-authlib.git/blobdiff_plain/d9898ee81388d1eff40cf71e85d9ce9b5c1401fe..d50284c4aa46bdb558ab92276d4e3ebd856fe9a6:/authmysqllib.c diff --git a/authmysqllib.c b/authmysqllib.c index dc76ef4..08ebdee 100644 --- a/authmysqllib.c +++ b/authmysqllib.c @@ -1,5 +1,5 @@ /* -** Copyright 2000-2005 Double Precision, Inc. See COPYING for +** Copyright 2000-2010 Double Precision, Inc. See COPYING for ** distribution information. */ @@ -20,95 +20,61 @@ #define err courier_auth_err -/* siefca@pld.org.pl */ -#define MAX_SUBSTITUTION_LEN 32 -#define SV_BEGIN_MARK "$(" -#define SV_END_MARK ")" -#define SV_BEGIN_LEN ((sizeof(SV_BEGIN_MARK))-1) -#define SV_END_LEN ((sizeof(SV_END_MARK))-1) - -static const char rcsid[]="$Id: authmysqllib.c,v 1.44 2007/02/10 02:51:06 mrsam Exp $"; - -/* siefca@pld.org.pl */ -struct var_data { - const char *name; - const char *value; - const size_t size; - size_t value_length; - } ; - -/* siefca@pld.org.pl */ -typedef int (*parsefunc)(const char *, size_t, void *); - static const char *read_env(const char *env) { -static char *mysqlauth=0; -static size_t mysqlauth_size=0; -size_t i; -char *p=0; -int l=strlen(env); + return authgetconfig(AUTHMYSQLRC, env); +} - if (!mysqlauth) - { - FILE *f=fopen(AUTHMYSQLRC, "r"); - struct stat buf; +static MYSQL mysql_buf; - if (!f) return (0); - if (fstat(fileno(f), &buf) || - (mysqlauth=malloc(buf.st_size+2)) == 0) - { - fclose(f); - return (0); - } - if (fread(mysqlauth, buf.st_size, 1, f) != 1) - { - free(mysqlauth); - mysqlauth=0; - fclose(f); - return (0); - } - mysqlauth[mysqlauth_size=buf.st_size]=0; - - for (i=0; i, VIAN, Ltd. */ +#if MYSQL_VERSION_ID >= 41000 + const char *character_set=read_env("MYSQL_CHARACTER_SET"), *check; + + if(character_set){ + + /* + * This function works like the SET NAMES statement, but also sets + * the value of mysql->charset, and thus affects the character set + * used by mysql_real_escape_string() + * + * (return value apparently work the opposite of what is documented) + */ + mysql_set_character_set(mysql, character_set); + check = mysql_character_set_name(mysql); + if (strcmp(character_set, check) != 0) + { + err("Cannot set MySQL character set \"%s\", working with \"%s\"\n", + character_set, check); + } + else + { + DPRINTF("Install of a character set for MySQL: %s", character_set); + } + } +#endif /* 41000 */ +} static int do_connect() { @@ -236,6 +202,16 @@ unsigned int use_ssl=0; mysql=0; return (-1); } + + DPRINTF("authmysqllib: connected. Versions: " + "header %lu, " + "client %lu, " + "server %lu", + (long)MYSQL_VERSION_ID, + mysql_get_client_version(), + mysql_get_server_version(mysql)); + + set_session_options(); return (0); } @@ -272,383 +248,11 @@ static void initui() memset(&ui, 0, sizeof(ui)); } -static void append_username(char *p, const char *username, - const char *defdomain) -{ - for (strcpy(p, username); *p; p++) - if (*p == '\'' || *p == '"' || *p == '\\' || - (int)(unsigned char)*p < ' ') - *p=' '; /* No funny business */ - if (strchr(username, '@') == 0 && defdomain && *defdomain) - strcat(strcpy(p, "@"), defdomain); -} - -/* siefca@pld.org.pl */ -static struct var_data *get_variable (const char *begin, size_t len, - struct var_data *vdt) -{ -struct var_data *vdp; - - if (!begin || !vdt) /* should never happend */ - { - err("authmysql: critical error while " - "parsing substitution variable"); - return NULL; - } - if (len < 1) - { - err("authmysql: unknown empty substitution " - "variable - aborting"); - return NULL; - } - if (len > MAX_SUBSTITUTION_LEN) - { - err("authmysql: variable name too long " - "while parsing substitution. " - "name begins with " - SV_BEGIN_MARK - "%.*s...", MAX_SUBSTITUTION_LEN, begin); - return NULL; - } - - for (vdp=vdt; vdp->name; vdp++) - if (vdp->size == len+1 && - !strncmp(begin, vdp->name, len)) - { - if (!vdp->value) - vdp->value = ""; - if (!vdp->value_length) /* length cache */ - vdp->value_length = strlen (vdp->value); - return vdp; - } - - err("authmysql: unknown substitution variable " - SV_BEGIN_MARK - "%.*s" - SV_END_MARK - , (int)len, begin); - - return NULL; -} - -/* siefca@pld.org.pl */ -static int ParsePlugin_counter (const char *p, size_t length, void *vp) -{ - if (!p || !vp || length < 0) - { - err("authmysql: bad arguments while counting " - "query string"); - return -1; - } - - *((size_t *)vp) += length; - - return 0; -} - -/* siefca@pld.org.pl */ -static int ParsePlugin_builder (const char *p, size_t length, void *vp) -{ -char **strptr = (char **) vp; - - if (!p || !vp || length < 0) - { - err("authmysql: bad arguments while building " - "query string"); - return -1; - } - - if (!length) return 0; - memcpy ((void *) *strptr, (void *) p, length); - *strptr += length; - - return 0; -} - -/* siefca@pld.org.pl */ -static int parse_core (const char *source, struct var_data *vdt, - parsefunc outfn, void *result) -{ -size_t v_size = 0, - t_size = 0; -const char *p, *q, *e, - *v_begin, *v_end, - *t_begin, *t_end; -struct var_data *v_ptr; - - if (!source) - source = ""; - if (!result) - { - err("authmysql: no memory allocated for result " - "while parser core was invoked"); - return -1; - } - if (!vdt) - { - err("authmysql: no substitution table found " - "while parser core was invoked"); - return -1; - } - - q = source; - while ( (p=strstr(q, SV_BEGIN_MARK)) ) - { - e = strstr (p, SV_END_MARK); - if (!e) - { - err("authmysql: syntax error in " - "substitution " - "- no closing symbol found! " - "bad variable begins with:" - "%.*s...", MAX_SUBSTITUTION_LEN, p); - return -1; - } - - /* - ** - ** __________sometext$(variable_name)_________ - ** | | | | - ** t_begin' t_end' `v_begin `v_end - ** - */ - - v_begin = p+SV_BEGIN_LEN; /* variable field ptr */ - v_end = e-SV_END_LEN; /* variable field last character */ - v_size = v_end-v_begin+1;/* variable field length */ - - t_begin = q; /* text field ptr */ - t_end = p-1; /* text field last character */ - t_size = t_end-t_begin+1;/* text field length */ - - /* work on text */ - if ( (outfn (t_begin, t_size, result)) == -1 ) - return -1; - - /* work on variable */ - v_ptr = get_variable (v_begin, v_size, vdt); - if (!v_ptr) return -1; - - if ( (outfn (v_ptr->value, v_ptr->value_length, result)) == -1 ) - return -1; - - q = e + 1; - } - - /* work on last part of text if any */ - if (*q != '\0') - if ( (outfn (q, strlen(q), result)) == -1 ) - return -1; - - return 0; -} - -/* siefca@pld.org.pl */ -static char *parse_string (const char *source, struct var_data *vdt) -{ -struct var_data *vdp = NULL; -char *output_buf = NULL, - *pass_buf = NULL; -size_t buf_size = 2; - - if (source == NULL || *source == '\0' || - vdt == NULL || vdt[0].name == NULL) - { - err("authmysql: source clause is empty " - "- this is critical error"); - return NULL; - } - - /* zero var_data length cache - important! */ - for (vdp=vdt; vdp->name; vdp++) - vdp->value_length = 0; - - - /* phase 1 - count and validate string */ - if ( (parse_core (source, vdt, &ParsePlugin_counter, &buf_size)) != 0) - return NULL; - - /* phase 2 - allocate memory */ - output_buf = malloc (buf_size); - if (!output_buf) - { - perror ("malloc"); - return NULL; - } - pass_buf = output_buf; - - /* phase 3 - build the output string */ - if ( (parse_core (source, vdt, &ParsePlugin_builder, &pass_buf)) != 0) - { - free (output_buf); - return NULL; - } - *pass_buf = '\0'; - - return output_buf; -} - -/* siefca@pld.org.pl */ -static const char *get_localpart (const char *username) -{ -size_t lbuf = 0; -const char *l_end, *p; -char *q; -static char localpart_buf[130]; - - if (!username || *username == '\0') return NULL; - - p = strchr(username,'@'); - if (p) - { - if ((p-username) > 128) - return NULL; - l_end = p; - } - else - { - if ((lbuf = strlen(username)) > 128) - return NULL; - l_end = username + lbuf; - } - - p=username; - q=localpart_buf; - - while (*p && p != l_end) - if (*p == '\"' || *p == '\\' || - *p == '\'' || (int)(unsigned char)*p < ' ') - p++; - else - *q++ = *p++; - - *q = '\0'; - return localpart_buf; -} - -/* siefca@pld.org.pl */ -static const char *get_domain (const char *username, const char *defdomain) -{ -static char domain_buf[260]; -const char *p; -char *q; - - if (!username || *username == '\0') return NULL; - p = strchr(username,'@'); - - if (!p || *(p+1) == '\0') - { - if (defdomain && *defdomain) - return defdomain; - else - return NULL; - } - - p++; - if ((strlen(p)) > 256) - return NULL; - - q = domain_buf; - while (*p) - if (*p == '\"' || *p == '\\' || - *p == '\'' || (int)(unsigned char)*p < ' ') - p++; - else - *q++ = *p++; - - *q = '\0'; - return domain_buf; -} - -/* siefca@pld.org.pl */ - -static const char *validateMyPassword (const char *password) -{ -static char pass_buf[2][540]; /* Use two buffers, see parse_chpass_clause */ -static int next_pass=0; -const char *p; -char *q, *endq; - - if (!password || *password == '\0' || (strlen(password)) > 256) - return NULL; - - next_pass= 1-next_pass; - - p = password; - q = pass_buf[next_pass]; - endq = q + sizeof pass_buf[next_pass]; - - while (*p && q < endq) - { - if (*p == '\"' || *p == '\\' || *p == '\'') - *q++ = '\\'; - *q++ = *p++; - } - - if (q >= endq) - return NULL; - - *q = '\0'; - return pass_buf[next_pass]; -} - -/* siefca@pld.org.pl */ -static char *parse_select_clause (const char *clause, const char *username, - const char *defdomain, - const char *service) -{ -static struct var_data vd[]={ - {"local_part", NULL, sizeof("local_part"), 0}, - {"domain", NULL, sizeof("domain"), 0}, - {"service", NULL, sizeof("service"), 0}, - {NULL, NULL, 0, 0}}; - - if (clause == NULL || *clause == '\0' || - !username || *username == '\0') - return NULL; - - vd[0].value = get_localpart (username); - vd[1].value = get_domain (username, defdomain); - if (!vd[0].value || !vd[1].value) - return NULL; - vd[2].value = service; - - return (parse_string (clause, vd)); -} - -/* siefca@pld.org.pl */ -static char *parse_chpass_clause (const char *clause, const char *username, - const char *defdomain, const char *newpass, - const char *newpass_crypt) -{ -static struct var_data vd[]={ - {"local_part", NULL, sizeof("local_part"), 0}, - {"domain", NULL, sizeof("domain"), 0}, - {"newpass", NULL, sizeof("newpass"), 0}, - {"newpass_crypt", NULL, sizeof("newpass_crypt"), 0}, - {NULL, NULL, 0, 0}}; - - if (clause == NULL || *clause == '\0' || - !username || *username == '\0' || - !newpass || *newpass == '\0' || - !newpass_crypt || *newpass_crypt == '\0') return NULL; - - vd[0].value = get_localpart (username); - vd[1].value = get_domain (username, defdomain); - vd[2].value = validateMyPassword (newpass); - vd[3].value = validateMyPassword (newpass_crypt); - - if (!vd[0].value || !vd[1].value || - !vd[2].value || !vd[3].value) return NULL; - - return (parse_string (clause, vd)); -} - struct authmysqluserinfo *auth_mysql_getuserinfo(const char *username, const char *service) { const char *defdomain =NULL; -char *querybuf, *p; +char *querybuf; MYSQL_ROW row; MYSQL_RES *result; int num_fields; @@ -656,8 +260,14 @@ char *endp; const char *select_clause; /* siefca@pld.org.pl */ -static const char query[]= - "SELECT %s, %s, %s, %s, %s, %s, %s, %s, %s, %s FROM %s WHERE %s = \""; +#define DEFAULT_SELECT_QUERY "SELECT %s, %s, %s, %s, %s, %s, %s, %s, %s, %s FROM %s WHERE %s = '%s%s%s' %s%s%s", \ + login_field, crypt_field, clear_field, uid_field,\ + gid_field, home_field, maildir_field, quota_field,\ + name_field, options_field, user_table, login_field,\ + username_escaped,\ + has_domain || !*defdomain ? "":"@", has_domain ? "":defdomain, \ + *where_clause ? " AND (":"", where_clause,\ + *where_clause ? ")":"" if (do_connect()) return (0); @@ -681,6 +291,10 @@ static const char query[]= *quota_field, *options_field, *where_clause; + char *username_escaped; + size_t query_size; + char dummy_buf[1]; + int has_domain; user_table=read_env("MYSQL_USER_TABLE"); @@ -731,47 +345,40 @@ static const char query[]= where_clause=read_env("MYSQL_WHERE_CLAUSE"); if (!where_clause) where_clause = ""; - querybuf=malloc(sizeof(query) + 100 - + 2 * strlen(login_field) - + strlen(crypt_field) - + strlen(clear_field) - + strlen(uid_field) + strlen(gid_field) - + strlen(home_field) - + strlen(maildir_field) - + strlen(quota_field) - + strlen(name_field) - + strlen(options_field) - + strlen(user_table) - + strlen(username) - + strlen(defdomain) - + strlen(where_clause)); + username_escaped=malloc(strlen(username)*2+1); - if (!querybuf) + if (!username_escaped) { perror("malloc"); return (0); } - sprintf(querybuf, query, login_field, crypt_field, clear_field, - uid_field, gid_field, home_field, maildir_field, quota_field, - name_field, options_field, user_table, login_field); + mysql_real_escape_string(mysql, username_escaped, + username, strlen(username)); - p=querybuf+strlen(querybuf); + has_domain=strchr(username, '@') != NULL; - append_username(p, username, defdomain); - strcat(p, "\""); - - if (strcmp(where_clause, "")) { - strcat(p, " AND ("); - strcat(p, where_clause); - strcat(p, ")"); + query_size=snprintf(dummy_buf, 1, DEFAULT_SELECT_QUERY); + + querybuf=malloc(query_size+1); + + if (!querybuf) + { + free(username_escaped); + perror("malloc"); + return(0); } + + snprintf(querybuf, query_size+1, DEFAULT_SELECT_QUERY); + free(username_escaped); + } else { /* siefca@pld.org.pl */ - querybuf=parse_select_clause (select_clause, username, - defdomain, service); + querybuf=auth_parse_select_clause (escape_str, + select_clause, username, + defdomain, service); if (!querybuf) { DPRINTF("parse_select_clause failed (DEFAULT_DOMAIN not set?)"); @@ -883,12 +490,12 @@ int auth_mysql_setpass(const char *user, const char *pass, const char *oldpass) { char *newpass_crypt; - const char *p; - int l; char *sql_buf; - const char *comma; int rc=0; + char *clear_escaped; + char *crypt_escaped; + const char *clear_field =NULL, *crypt_field =NULL, *defdomain =NULL, @@ -897,116 +504,122 @@ int auth_mysql_setpass(const char *user, const char *pass, *login_field =NULL, *chpass_clause =NULL; /* siefca@pld.org.pl */ - if (!mysql) - return (-1); - + if (do_connect()) return (-1); if (!(newpass_crypt=authcryptpasswd(pass, oldpass))) return (-1); - for (l=0, p=pass; *p; p++) + clear_escaped=malloc(strlen(pass)*2+1); + + if (!clear_escaped) { - if ((int)(unsigned char)*p < ' ') - { - free(newpass_crypt); - return (-1); - } - if (*p == '"' || *p == '\\') - ++l; - ++l; + perror("malloc"); + free(newpass_crypt); + return -1; } + crypt_escaped=malloc(strlen(newpass_crypt)*2+1); + + if (!crypt_escaped) + { + perror("malloc"); + free(clear_escaped); + free(newpass_crypt); + return -1; + } + + mysql_real_escape_string(mysql, clear_escaped, pass, strlen(pass)); + mysql_real_escape_string(mysql, crypt_escaped, + newpass_crypt, strlen(newpass_crypt)); + /* siefca@pld.org.pl */ chpass_clause=read_env("MYSQL_CHPASS_CLAUSE"); defdomain=read_env("DEFAULT_DOMAIN"); user_table=read_env("MYSQL_USER_TABLE"); if (!chpass_clause) { + int has_domain=strchr(user, '@') != NULL; + char *username_escaped; + char dummy_buf[1]; + size_t sql_buf_size; + + username_escaped=malloc(strlen(user)*2+1); + + if (!username_escaped) + { + perror("malloc"); + free(clear_escaped); + free(crypt_escaped); + free(newpass_crypt); + return -1; + } + + mysql_real_escape_string(mysql, username_escaped, + user, strlen(user)); + login_field = read_env("MYSQL_LOGIN_FIELD"); if (!login_field) login_field = "id"; crypt_field=read_env("MYSQL_CRYPT_PWFIELD"); clear_field=read_env("MYSQL_CLEAR_PWFIELD"); where_clause=read_env("MYSQL_WHERE_CLAUSE"); - sql_buf=malloc(strlen(crypt_field ? crypt_field:"") - + strlen(clear_field ? clear_field:"") - + strlen(defdomain ? defdomain:"") - + strlen(login_field) + l + strlen(newpass_crypt) - + strlen(user_table) - + strlen(where_clause ? where_clause:"") - + 200); - } - else - { - sql_buf=parse_chpass_clause(chpass_clause, - user, - defdomain, - pass, - newpass_crypt); - } - - if (!sql_buf) - { - free(newpass_crypt); - return (-1); - } + if (!where_clause) + where_clause=""; - if (!chpass_clause) /*siefca@pld.org.pl */ - { - sprintf(sql_buf, "UPDATE %s SET", user_table); - - comma=""; + if (!crypt_field) + crypt_field=""; - if (clear_field && *clear_field) - { - char *q; + if (!clear_field) + clear_field=""; - strcat(strcat(strcat(sql_buf, " "), clear_field), - "=\""); + if (!defdomain) + defdomain=""; - q=sql_buf+strlen(sql_buf); - while (*pass) - { - if (*pass == '"' || *pass == '\\') - *q++= '\\'; - *q++ = *pass++; - } - strcpy(q, "\""); - comma=", "; - } +#define DEFAULT_SETPASS_UPDATE \ + "UPDATE %s SET %s%s%s%s %s %s%s%s%s WHERE %s='%s%s%s' %s%s%s", \ + user_table, \ + *clear_field ? clear_field:"", \ + *clear_field ? "='":"", \ + *clear_field ? clear_escaped:"", \ + *clear_field ? "'":"", \ + \ + *clear_field && *crypt_field ? ",":"", \ + \ + *crypt_field ? crypt_field:"", \ + *crypt_field ? "='":"", \ + *crypt_field ? crypt_escaped:"", \ + *crypt_field ? "'":"", \ + login_field, \ + username_escaped, \ + has_domain || !*defdomain ? "":"@", \ + has_domain ? "":defdomain, \ + *where_clause ? " AND (":"", where_clause, \ + *where_clause ? ")":"" - if (crypt_field && *crypt_field) - { - strcat(strcat(strcat(strcat(strcat(strcat(sql_buf, comma), - " "), - crypt_field), - "=\""), - newpass_crypt), - "\""); - } - free(newpass_crypt); - strcat(strcat(strcat(sql_buf, " WHERE "), - login_field), - "=\""); + sql_buf_size=snprintf(dummy_buf, 1, DEFAULT_SETPASS_UPDATE); - append_username(sql_buf+strlen(sql_buf), user, defdomain); + sql_buf=malloc(sql_buf_size+1); - strcat(sql_buf, "\""); + if (sql_buf) + snprintf(sql_buf, sql_buf_size+1, + DEFAULT_SETPASS_UPDATE); - if (where_clause && *where_clause) - { - strcat(sql_buf, " AND ("); - strcat(sql_buf, where_clause); - strcat(sql_buf, ")"); - } - + free(username_escaped); } else { - free(newpass_crypt); + sql_buf=auth_parse_chpass_clause(escape_str, + chpass_clause, + user, + defdomain, + clear_escaped, + crypt_escaped); } - + + free(clear_escaped); + free(crypt_escaped); + free(newpass_crypt); if (courier_authdebug_login_level >= 2) { @@ -1032,14 +645,15 @@ void auth_mysql_enumerate( void(*cb_func)(const char *name, void *void_arg) { const char *defdomain, *select_clause; - char *querybuf, *p; + char *querybuf; MYSQL_ROW row; MYSQL_RES *result; - static const char query[]= - "SELECT %s, %s, %s, %s, %s, %s FROM %s WHERE 1=1"; - - if (do_connect()) return; + if (do_connect()) + { + (*cb_func)(NULL, 0, 0, NULL, NULL, NULL, void_arg); + return; + } initui(); @@ -1058,6 +672,8 @@ void auth_mysql_enumerate( void(*cb_func)(const char *name, *maildir_field, *options_field, *where_clause; + char dummy_buf[1]; + size_t query_len; user_table=read_env("MYSQL_USER_TABLE"); @@ -1089,14 +705,18 @@ void auth_mysql_enumerate( void(*cb_func)(const char *name, where_clause=read_env("MYSQL_WHERE_CLAUSE"); if (!where_clause) where_clause = ""; - querybuf=malloc(sizeof(query) + 100 - + strlen(login_field) - + strlen(uid_field) + strlen(gid_field) - + strlen(home_field) - + strlen(maildir_field) - + strlen(options_field) - + strlen(user_table) - + strlen(where_clause)); + +#define DEFAULT_ENUMERATE_QUERY \ + "SELECT %s, %s, %s, %s, %s, %s FROM %s %s%s",\ + login_field, uid_field, gid_field, \ + home_field, maildir_field, \ + options_field, user_table, \ + *where_clause ? " WHERE ":"", \ + where_clause + + query_len=snprintf(dummy_buf, 1, DEFAULT_ENUMERATE_QUERY); + + querybuf=malloc(query_len+1); if (!querybuf) { @@ -1104,23 +724,14 @@ void auth_mysql_enumerate( void(*cb_func)(const char *name, return; } - sprintf(querybuf, query, login_field, - uid_field, gid_field, home_field, maildir_field, - options_field, user_table); - - p=querybuf+strlen(querybuf); - - if (strcmp(where_clause, "")) { - strcat(p, " AND ("); - strcat(p, where_clause); - strcat(p, ")"); - } + snprintf(querybuf, query_len+1, DEFAULT_ENUMERATE_QUERY); } else { /* siefca@pld.org.pl */ - querybuf=parse_select_clause (select_clause, "*", - defdomain, "enumerate"); + querybuf=auth_parse_select_clause (escape_str, + select_clause, "*", + defdomain, "enumerate"); if (!querybuf) { DPRINTF("authmysql: parse_select_clause failed"); @@ -1165,9 +776,9 @@ void auth_mysql_enumerate( void(*cb_func)(const char *name, while ((row = mysql_fetch_row (result)) != NULL) { if(!row[0] || !row[0][0] - || !row[1] || !row[1][0] - || !row[2] || !row[2][0] - || !row[3] || !row[3][0]) + || !row[1] || !row[1][0] + || !row[2] || !row[2][0] + || !row[3] || !row[3][0]) { continue; }