1 /*  Copyright 2004-2007 Thimo Eichstaedt <apache-mod@digithi.de>
2  *
3  *  Licensed under the Apache License, Version 2.0 (the "License");
4  *  you may not use this file except in compliance with the License.
5  *  You may obtain a copy of the License at
6  *
7  *      http://www.apache.org/licenses/LICENSE-2.0
8  *
9  *  Unless required by applicable law or agreed to in writing, software
10  *  distributed under the License is distributed on an "AS IS" BASIS,
11  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  *  See the License for the specific language governing permissions and
13  *  limitations under the License.
14  *
15  *  Based on code from
16  *      Dirk.vanGulik@jrc.it  - original module
17  *      mjd@pobox.com         - bug fixes, conversion from mSQL to MySQL
18  *
19  */
20 
21 /********************************************************************************
22  *                                includes                                      *
23  ********************************************************************************/
24 #include <string.h>
25 #include <mysql.h>
26 #include "apr_strings.h"
27 #include "httpd.h"
28 #include "http_config.h"
29 #include "http_core.h"
30 #include "http_request.h"
31 #include "http_log.h"
32 
33 #include "mod_auth_cookie_sql2.h"
34 
35 static MYSQL *dbh = NULL;
36 
37 /********************************************************************************
38  *                               functions                                      *
39  ********************************************************************************/
40 
41 /* try to open connection */
open_db(auth_cookie_sql2_config_rec * conf,request_rec * r)42 int open_db(auth_cookie_sql2_config_rec *conf, request_rec *r) {
43 
44     if (dbh != NULL) {
45 	if (mysql_ping(dbh) == 0) {
46 	    return RET_OK;
47 	} else {
48 	    ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, r, ERRTAG "database connection died, trying to establish a new one.");
49 	    mysql_close(dbh);
50 	    dbh = NULL;
51 	}
52     }
53 
54     if ((dbh=mysql_init(NULL)) == NULL) {
55 	return RET_ERR;
56     }
57 
58     mysql_options(dbh,MYSQL_READ_DEFAULT_GROUP,MY_MYSQL_APPNAME);
59 
60     if (mysql_real_connect(dbh, conf->dbhost, conf->dbuser, conf->dbpassword, conf->dbname, 0, NULL, 0) == NULL) {
61 	ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, r, ERRTAG "couldn't connect to database: %s", mysql_error(dbh));
62 
63 	return RET_ERR;
64     } else {
65 	return RET_OK;
66     }
67 }
68 
69 /* close the database connection */
close_db(auth_cookie_sql2_config_rec * conf,request_rec * r,int force)70 int close_db(auth_cookie_sql2_config_rec *conf, request_rec *r, int force) {
71     if (dbh == NULL) {
72 	// do nothing
73     } else if (conf == NULL) {
74 	mysql_close(dbh);
75 	dbh=NULL;
76     } else if (conf->dbpersistent == 0 || force == 1) {
77 	mysql_close(dbh);
78 	dbh=NULL;
79     }
80 
81     return RET_OK;
82 }
83 
84 /* check given cookie against db */
check_against_db(auth_cookie_sql2_config_rec * conf,request_rec * r,char * cookiename,char * cookieval,char * username,char * remoteip,char * addon,time_t tc)85 int check_against_db(auth_cookie_sql2_config_rec *conf, request_rec *r, char *cookiename, char *cookieval, char *username, char *remoteip, char *addon, time_t tc) {
86     MYSQL_RES *res;
87     MYSQL_ROW row;
88     apr_pool_t *p = r->pool;
89     char *esc_cookiename, *esc_cookieval;
90     char *query, *queryopt;
91     int ulen;
92     int ret=RET_ERR; // default
93 
94     if (open_db(conf,r) != RET_OK) {
95 	ret=RET_ERR;
96 	goto check_against_db_mysql_close;
97     }
98 
99     /* escape cookiename, size of cookiename can grow double as big as before */
100     ulen=strlen(cookiename);
101     if (! (esc_cookiename = apr_palloc(p, (ulen*2) + 1))) {
102 	ret=RET_ERR;
103 	goto check_against_db_mysql_close;
104     }
105 
106     mysql_real_escape_string(dbh, esc_cookiename, cookiename, ulen);
107 
108     /* escape cookieval, can grow to double size, too */
109     ulen=strlen(cookieval);
110     if ((esc_cookieval = apr_palloc(p, (ulen*2) + 1)) == NULL) {
111 	ret=RET_ERR;
112 	goto check_against_db_mysql_close;
113     }
114 
115     mysql_real_escape_string(dbh, esc_cookieval, cookieval, ulen);
116 
117     /* prepare query string, queryopt contains optional arguments */
118     if ((queryopt = apr_palloc(r->pool,sizeof(char))) == NULL) {
119 	ret=RET_ERR;
120 	goto check_against_db_mysql_close;
121     }
122 
123     *queryopt = '\0';
124 
125     /* Check for cookie Expiry ? */
126     if (conf->dbexpiry_field) {
127 	queryopt = apr_psprintf(p, "%s AND %s > %lu", queryopt, conf->dbexpiry_field,tc);
128     }
129 
130     /* Check for right remote ip ? */
131     if (conf->dbremoteip_field) {
132 	queryopt = apr_psprintf(p, "%s AND %s='%s'", queryopt, conf->dbremoteip_field, remoteip);
133     }
134 
135     if (addon) {
136 	queryopt = apr_psprintf(p,"%s %s", queryopt, addon);
137     }
138 
139     /* Generate query */
140     query = apr_psprintf(p, "SELECT %s FROM %s WHERE %s='%s' AND %s='%s'%s",
141 	    conf->dbusername_field,
142 	    conf->dbtable,
143 	    conf->dbsessname_field,
144 	    esc_cookiename,
145 	    conf->dbsessval_field,
146 	    esc_cookieval,
147 	    queryopt);
148 
149     if (query == NULL) {
150 	ret=RET_ERR;
151 	goto check_against_db_mysql_close;
152     }
153 
154     if (mysql_query(dbh, query) != 0) {
155 	ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, r, ERRTAG "error in MySQL query \"%s\": %s", query, mysql_error(dbh));
156 
157 	ret=RET_ERR;
158 	goto check_against_db_mysql_close;
159     }
160 
161     if ((res = mysql_store_result(dbh)) == NULL) {
162 	ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, r, ERRTAG "couldn't store query result: %s", mysql_error(dbh));
163 
164 	ret=RET_ERR;
165 	goto check_against_db_mysql_close;
166     }
167 
168     /* if any other number of results than 1 found, clean up and return with 0 */
169     if (mysql_num_rows(res) != 1) {
170 
171 	ret=RET_UNAUTHORIZED;
172 	goto check_against_db_mysql_free_result;
173     }
174 
175     /* got a result, fetch row */
176     if ((row = mysql_fetch_row(res)) == NULL) {
177 	ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, r, ERRTAG "couldn't fetch row: %s", mysql_error(dbh));
178 
179 	ret=RET_ERR;
180 	goto check_against_db_mysql_free_result;
181     }
182 
183     /* check for length of row */
184     if (strlen (row[0]) > MAX_USERNAME_LEN) {
185 	ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, r, ERRTAG "fetched username from DB, but is longer than max length %d", MAX_USERNAME_LEN);
186 
187 	ret=RET_ERR;
188 	goto check_against_db_mysql_free_result;
189     }
190 
191     /* all tests passed, copy content of row[0] to username */
192     strcpy(username, row[0]);
193     ret=RET_AUTHORIZED;
194 
195 check_against_db_mysql_free_result:
196     mysql_free_result(res);
197 
198 check_against_db_mysql_close:
199     close_db(conf,r,0);
200     return ret;
201 }
202