1/* Licensed to the Apache Software Foundation (ASF) under one or more 2 * contributor license agreements. See the NOTICE file distributed with 3 * this work for additional information regarding copyright ownership. 4 * The ASF licenses this file to You under the Apache License, Version 2.0 5 * (the "License"); you may not use this file except in compliance with 6 * the License. You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17/* 18 * mod_ident: Handle RFC 1413 ident request 19 * obtained from rfc1413.c 20 * 21 * rfc1413() speaks a common subset of the RFC 1413, AUTH, TAP and IDENT 22 * protocols. The code queries an RFC 1413 etc. compatible daemon on a remote 23 * host to look up the owner of a connection. The information should not be 24 * used for authentication purposes. This routine intercepts alarm signals. 25 * 26 * Author: Wietse Venema, Eindhoven University of Technology, 27 * The Netherlands. 28 */ 29 30/* Some small additions for Apache --- ditch the "sccsid" var if 31 * compiling with gcc (it *has* changed), include ap_config.h for the 32 * prototypes it defines on at least one system (SunlOSs) which has 33 * them missing from the standard header files, and one minor change 34 * below (extra parens around assign "if (foo = bar) ..." to shut up 35 * gcc -Wall). 36 */ 37 38/* Rewritten by David Robinson */ 39 40#include "apr.h" 41#include "apr_network_io.h" 42#include "apr_strings.h" 43#include "apr_optional.h" 44 45#define APR_WANT_STDIO 46#define APR_WANT_STRFUNC 47#include "apr_want.h" 48 49#include "httpd.h" /* for server_rec, conn_rec, etc. */ 50#include "http_config.h" 51#include "http_core.h" 52#include "http_log.h" /* for aplog_error */ 53#include "util_ebcdic.h" 54 55/* Whether we should enable rfc1413 identity checking */ 56#ifndef DEFAULT_RFC1413 57#define DEFAULT_RFC1413 0 58#endif 59 60#define RFC1413_UNSET 2 61 62/* request timeout (sec) */ 63#ifndef RFC1413_TIMEOUT 64#define RFC1413_TIMEOUT 30 65#endif 66 67/* Local stuff. */ 68 69/* Semi-well-known port */ 70#define RFC1413_PORT 113 71 72/* maximum allowed length of userid */ 73#define RFC1413_USERLEN 512 74 75/* rough limit on the amount of data we accept. */ 76#define RFC1413_MAXDATA 1000 77 78/* default username, if it could not determined */ 79#define FROM_UNKNOWN "unknown" 80 81typedef struct { 82 int do_rfc1413; 83 int timeout_unset; 84 apr_time_t timeout; 85} ident_config_rec; 86 87static apr_status_t rfc1413_connect(apr_socket_t **newsock, conn_rec *conn, 88 server_rec *srv, apr_time_t timeout) 89{ 90 apr_status_t rv; 91 apr_sockaddr_t *localsa, *destsa; 92 93 if ((rv = apr_sockaddr_info_get(&localsa, conn->local_ip, APR_UNSPEC, 94 0, /* ephemeral port */ 95 0, conn->pool)) != APR_SUCCESS) { 96 /* This should not fail since we have a numeric address string 97 * as the host. */ 98 ap_log_error(APLOG_MARK, APLOG_CRIT, rv, srv, APLOGNO(01492) 99 "rfc1413: apr_sockaddr_info_get(%s) failed", 100 conn->local_ip); 101 return rv; 102 } 103 104 if ((rv = apr_sockaddr_info_get(&destsa, conn->client_ip, 105 localsa->family, /* has to match */ 106 RFC1413_PORT, 0, conn->pool)) != APR_SUCCESS) { 107 /* This should not fail since we have a numeric address string 108 * as the host. */ 109 ap_log_error(APLOG_MARK, APLOG_CRIT, rv, srv, APLOGNO(01493) 110 "rfc1413: apr_sockaddr_info_get(%s) failed", 111 conn->client_ip); 112 return rv; 113 } 114 115 if ((rv = apr_socket_create(newsock, 116 localsa->family, /* has to match */ 117 SOCK_STREAM, 0, conn->pool)) != APR_SUCCESS) { 118 ap_log_error(APLOG_MARK, APLOG_CRIT, rv, srv, APLOGNO(01494) 119 "rfc1413: error creating query socket"); 120 return rv; 121 } 122 123 if ((rv = apr_socket_timeout_set(*newsock, timeout)) != APR_SUCCESS) { 124 ap_log_error(APLOG_MARK, APLOG_CRIT, rv, srv, APLOGNO(01495) 125 "rfc1413: error setting query socket timeout"); 126 apr_socket_close(*newsock); 127 return rv; 128 } 129 130/* 131 * Bind the local and remote ends of the query socket to the same 132 * IP addresses as the connection under investigation. We go 133 * through all this trouble because the local or remote system 134 * might have more than one network address. The RFC1413 etc. 135 * client sends only port numbers; the server takes the IP 136 * addresses from the query socket. 137 */ 138 139 if ((rv = apr_socket_bind(*newsock, localsa)) != APR_SUCCESS) { 140 ap_log_error(APLOG_MARK, APLOG_CRIT, rv, srv, APLOGNO(01496) 141 "rfc1413: Error binding query socket to local port"); 142 apr_socket_close(*newsock); 143 return rv; 144 } 145 146/* 147 * errors from connect usually imply the remote machine doesn't support 148 * the service; don't log such an error 149 */ 150 if ((rv = apr_socket_connect(*newsock, destsa)) != APR_SUCCESS) { 151 apr_socket_close(*newsock); 152 return rv; 153 } 154 155 return APR_SUCCESS; 156} 157 158static apr_status_t rfc1413_query(apr_socket_t *sock, conn_rec *conn, 159 server_rec *srv) 160{ 161 apr_port_t rmt_port, our_port; 162 apr_port_t sav_rmt_port, sav_our_port; 163 apr_size_t i; 164 char *cp; 165 char buffer[RFC1413_MAXDATA + 1]; 166 char user[RFC1413_USERLEN + 1]; /* XXX */ 167 apr_size_t buflen; 168 169 sav_our_port = conn->local_addr->port; 170 sav_rmt_port = conn->client_addr->port; 171 172 /* send the data */ 173 buflen = apr_snprintf(buffer, sizeof(buffer), "%hu,%hu\r\n", sav_rmt_port, 174 sav_our_port); 175 ap_xlate_proto_to_ascii(buffer, buflen); 176 177 /* send query to server. Handle short write. */ 178 i = 0; 179 while (i < buflen) { 180 apr_size_t j = strlen(buffer + i); 181 apr_status_t status; 182 status = apr_socket_send(sock, buffer+i, &j); 183 if (status != APR_SUCCESS) { 184 ap_log_error(APLOG_MARK, APLOG_CRIT, status, srv, APLOGNO(01497) 185 "write: rfc1413: error sending request"); 186 return status; 187 } 188 else if (j > 0) { 189 i+=j; 190 } 191 } 192 193 /* 194 * Read response from server. - the response should be newline 195 * terminated according to rfc - make sure it doesn't stomp its 196 * way out of the buffer. 197 */ 198 199 i = 0; 200 memset(buffer, '\0', sizeof(buffer)); 201 /* 202 * Note that the strchr function below checks for \012 instead of '\n' 203 * this allows it to work on both ASCII and EBCDIC machines. 204 */ 205 while((cp = strchr(buffer, '\012')) == NULL && i < sizeof(buffer) - 1) { 206 apr_size_t j = sizeof(buffer) - 1 - i; 207 apr_status_t status; 208 status = apr_socket_recv(sock, buffer+i, &j); 209 if (status != APR_SUCCESS) { 210 ap_log_error(APLOG_MARK, APLOG_CRIT, status, srv, APLOGNO(01498) 211 "read: rfc1413: error reading response"); 212 return status; 213 } 214 else if (j > 0) { 215 i+=j; 216 } 217 else if (status == APR_SUCCESS && j == 0) { 218 /* Oops... we ran out of data before finding newline */ 219 return APR_EINVAL; 220 } 221 } 222 223/* RFC1413_USERLEN = 512 */ 224 ap_xlate_proto_from_ascii(buffer, i); 225 if (sscanf(buffer, "%hu , %hu : USERID :%*[^:]:%512s", &rmt_port, &our_port, 226 user) != 3 || sav_rmt_port != rmt_port 227 || sav_our_port != our_port) 228 return APR_EINVAL; 229 230 /* 231 * Strip trailing carriage return. It is part of the 232 * protocol, not part of the data. 233 */ 234 235 if ((cp = strchr(user, '\r'))) 236 *cp = '\0'; 237 238 conn->remote_logname = apr_pstrdup(conn->pool, user); 239 240 return APR_SUCCESS; 241} 242 243static const char *set_idcheck(cmd_parms *cmd, void *d_, int arg) 244{ 245 ident_config_rec *d = d_; 246 247 d->do_rfc1413 = arg ? 1 : 0; 248 return NULL; 249} 250 251static const char *set_timeout(cmd_parms *cmd, void *d_, const char *arg) 252{ 253 ident_config_rec *d = d_; 254 255 d->timeout = apr_time_from_sec(atoi(arg)); 256 d->timeout_unset = 0; 257 return NULL; 258} 259 260static void *create_ident_dir_config(apr_pool_t *p, char *d) 261{ 262 ident_config_rec *conf = apr_palloc(p, sizeof(*conf)); 263 264 conf->do_rfc1413 = DEFAULT_RFC1413 | RFC1413_UNSET; 265 conf->timeout = apr_time_from_sec(RFC1413_TIMEOUT); 266 conf->timeout_unset = 1; 267 268 return (void *)conf; 269} 270 271static void *merge_ident_dir_config(apr_pool_t *p, void *old_, void *new_) 272{ 273 ident_config_rec *conf = (ident_config_rec *)apr_pcalloc(p, sizeof(*conf)); 274 ident_config_rec *old = (ident_config_rec *) old_; 275 ident_config_rec *new = (ident_config_rec *) new_; 276 277 conf->timeout = new->timeout_unset 278 ? old->timeout 279 : new->timeout; 280 281 conf->do_rfc1413 = new->do_rfc1413 & RFC1413_UNSET 282 ? old->do_rfc1413 283 : new->do_rfc1413; 284 285 return (void *)conf; 286} 287 288static const command_rec ident_cmds[] = 289{ 290 AP_INIT_FLAG("IdentityCheck", set_idcheck, NULL, RSRC_CONF|ACCESS_CONF, 291 "Enable identd (RFC 1413) user lookups - SLOW"), 292 AP_INIT_TAKE1("IdentityCheckTimeout", set_timeout, NULL, 293 RSRC_CONF|ACCESS_CONF, 294 "Identity check (RFC 1413) timeout duration (sec)"), 295 {NULL} 296}; 297 298module AP_MODULE_DECLARE_DATA ident_module; 299 300/* 301 * Optional function for the core to to the actual ident request 302 */ 303static const char *ap_ident_lookup(request_rec *r) 304{ 305 ident_config_rec *conf; 306 apr_socket_t *sock; 307 apr_status_t rv; 308 conn_rec *conn = r->connection; 309 server_rec *srv = r->server; 310 311 conf = ap_get_module_config(r->per_dir_config, &ident_module); 312 313 /* return immediately if ident requests are disabled */ 314 if (!(conf->do_rfc1413 & ~RFC1413_UNSET)) { 315 return NULL; 316 } 317 318 rv = rfc1413_connect(&sock, conn, srv, conf->timeout); 319 if (rv == APR_SUCCESS) { 320 rv = rfc1413_query(sock, conn, srv); 321 apr_socket_close(sock); 322 } 323 if (rv != APR_SUCCESS) { 324 conn->remote_logname = FROM_UNKNOWN; 325 } 326 327 return (const char *)conn->remote_logname; 328} 329 330static void register_hooks(apr_pool_t *p) 331{ 332 APR_REGISTER_OPTIONAL_FN(ap_ident_lookup); 333} 334 335AP_DECLARE_MODULE(ident) = 336{ 337 STANDARD20_MODULE_STUFF, 338 create_ident_dir_config, /* dir config creater */ 339 merge_ident_dir_config, /* dir merger --- default is to override */ 340 NULL, /* server config */ 341 NULL, /* merge server config */ 342 ident_cmds, /* command apr_table_t */ 343 register_hooks /* register hooks */ 344}; 345