X-Git-Url: https://git.hcoop.net/hcoop/debian/courier-authlib.git/blobdiff_plain/dd184caf1b0d37f50ea0ddcc68822bd38da32105..ac40fd9eb9d1980c90dc009d526a23ead1ec0f76:/authmysqllib.c diff --git a/authmysqllib.c b/authmysqllib.c index 40be961..e7a57c5 100644 --- a/authmysqllib.c +++ b/authmysqllib.c @@ -1,5 +1,5 @@ /* -** Copyright 2000-2005 Double Precision, Inc. See COPYING for +** Copyright 2000-2008 Double Precision, Inc. See COPYING for ** distribution information. */ @@ -27,7 +27,7 @@ #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.45 2007/10/07 02:50:45 mrsam Exp $"; +static const char rcsid[]="$Id: authmysqllib.c,v 1.49 2008/06/08 16:40:36 mrsam Exp $"; /* siefca@pld.org.pl */ struct var_data { @@ -110,6 +110,39 @@ static MYSQL mysql_buf; static MYSQL *mysql=0; +static void set_session_options(void) +/* +* session variables can be set once for the whole session +*/ +{ +/* Anton Dobkin , 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() { const char *server; @@ -236,6 +269,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,17 +315,6 @@ 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) @@ -488,108 +520,63 @@ size_t buf_size = 2; return output_buf; } -/* siefca@pld.org.pl */ -static const char *get_localpart (const char *username) +static char *local_part_escaped(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 + const char *p=strchr(username, '@'); + size_t n=p ? p-username:strlen(username); + char *buf=malloc(n*2+1); + + if (!buf) { - if ((lbuf = strlen(username)) > 128) - return NULL; - l_end = username + lbuf; + perror("malloc"); + return NULL; } - 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; + mysql_real_escape_string(mysql, buf, username, n); + return buf; } -/* siefca@pld.org.pl */ -static const char *get_domain (const char *username, const char *defdomain) +static char *domain_part_escaped(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; - } + const char *p=strchr(username, '@'); + size_t n; + char *buf; - p++; - if ((strlen(p)) > 256) + if (p) + ++p; + else + p=defdomain; + + n=strlen(p); + + buf=malloc(n*2+1); + + if (!buf) + { + perror("malloc"); return NULL; - - q = domain_buf; - while (*p) - if (*p == '\"' || *p == '\\' || - *p == '\'' || (int)(unsigned char)*p < ' ') - p++; - else - *q++ = *p++; + } - *q = '\0'; - return domain_buf; + mysql_real_escape_string(mysql, buf, p, n); + return buf; } -/* siefca@pld.org.pl */ - -static const char *validateMyPassword (const char *password) +static int local_and_domain_part_escaped(const char *username, + const char *defdomain, + char **local_ret, + char **domain_ret) { -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; + if ((*local_ret=local_part_escaped(username)) == NULL) + return 0; - p = password; - q = pass_buf[next_pass]; - endq = q + sizeof pass_buf[next_pass]; - - while (*p && q < endq) + if ((*domain_ret=domain_part_escaped(username, defdomain)) == NULL) { - if (*p == '\"' || *p == '\\' || *p == '\'') - *q++ = '\\'; - *q++ = *p++; + free(*local_ret); + return 0; } - - if (q >= endq) - return NULL; - - *q = '\0'; - return pass_buf[next_pass]; + + return 1; } /* siefca@pld.org.pl */ @@ -597,23 +584,33 @@ 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}}; + char *str; + + 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}}; + + char *l_part; + char *d_part; 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) + + if (!local_and_domain_part_escaped(username, defdomain, + &l_part, &d_part)) return NULL; + + vd[0].value=l_part; + vd[1].value=d_part; vd[2].value = service; - return (parse_string (clause, vd)); + str=parse_string (clause, vd); + free(l_part); + free(d_part); + return str; } /* siefca@pld.org.pl */ @@ -621,34 +618,50 @@ 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}}; + char *str; + + 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}}; + char *l_part; + char *d_part; 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 (!local_and_domain_part_escaped(username, defdomain, + &l_part, &d_part)) + return NULL; + + vd[0].value=l_part; + vd[1].value=d_part; + vd[2].value = newpass; + vd[3].value = newpass_crypt; if (!vd[0].value || !vd[1].value || - !vd[2].value || !vd[3].value) return NULL; + !vd[2].value || !vd[3].value) + { + free(l_part); + free(d_part); + return NULL; + } - return (parse_string (clause, vd)); + str=parse_string (clause, vd); + free(l_part); + free(d_part); + return str; } 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 +669,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 +700,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,41 +754,33 @@ 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 { @@ -779,43 +794,6 @@ static const char query[]= } } -/* Anton Dobkin , VIAN, Ltd. */ -#if MYSQL_VERSION_ID >= 41000 - const char *character_set=read_env("MYSQL_CHARACTER_SET"); - - if(character_set){ - - char *character_set_buf; - - character_set_buf=malloc(strlen(character_set)+11); - - if (!character_set_buf) - { - perror("malloc"); - return (0); - } - - strcpy(character_set_buf, "SET NAMES "); - strcat(character_set_buf, character_set); - - DPRINTF("Install of a character set for MySQL. SQL query: SET NAMES %s", character_set); - - if(mysql_query (mysql, character_set_buf)) - { - err("Install of a character set for MySQL is failed: %s MYSQL_CHARACTER_SET: may be invalid character set", mysql_error(mysql)); - auth_mysql_cleanup(); - - if (do_connect()) - { - free(character_set_buf); - return (0); - } - } - - free(character_set_buf); - } -#endif - DPRINTF("SQL query: %s", querybuf); if (mysql_query (mysql, querybuf)) { @@ -920,12 +898,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, @@ -934,116 +912,119 @@ 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); + if (!crypt_field) + crypt_field=""; - comma=""; + if (!clear_field) + clear_field=""; - if (clear_field && *clear_field) - { - char *q; - strcat(strcat(strcat(sql_buf, " "), clear_field), - "=\""); +#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 ? ")":"" - q=sql_buf+strlen(sql_buf); - while (*pass) - { - if (*pass == '"' || *pass == '\\') - *q++= '\\'; - *q++ = *pass++; - } - strcpy(q, "\""); - comma=", "; - } - 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=parse_chpass_clause(chpass_clause, + user, + defdomain, + clear_escaped, + crypt_escaped); } - + + free(clear_escaped); + free(crypt_escaped); + free(newpass_crypt); if (courier_authdebug_login_level >= 2) { @@ -1069,13 +1050,10 @@ 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; initui(); @@ -1095,6 +1073,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"); @@ -1126,14 +1106,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) { @@ -1141,17 +1125,7 @@ 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 { @@ -1202,9 +1176,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; }