1 2 /* 3 * Copyright (C) Igor Sysoev 4 * Copyright (C) Nginx, Inc. 5 */ 6 7 8 #include <ngx_config.h> 9 #include <ngx_core.h> 10 #include <ngx_http.h> 11 #include <ngx_crypt.h> 12 13 14 #define NGX_HTTP_AUTH_BUF_SIZE 2048 15 16 17 typedef struct { 18 ngx_http_complex_value_t *realm; 19 ngx_http_complex_value_t user_file; 20 } ngx_http_auth_basic_loc_conf_t; 21 22 23 static ngx_int_t ngx_http_auth_basic_handler(ngx_http_request_t *r); 24 static ngx_int_t ngx_http_auth_basic_crypt_handler(ngx_http_request_t *r, 25 ngx_str_t *passwd, ngx_str_t *realm); 26 static ngx_int_t ngx_http_auth_basic_set_realm(ngx_http_request_t *r, 27 ngx_str_t *realm); 28 static void ngx_http_auth_basic_close(ngx_file_t *file); 29 static void *ngx_http_auth_basic_create_loc_conf(ngx_conf_t *cf); 30 static char *ngx_http_auth_basic_merge_loc_conf(ngx_conf_t *cf, 31 void *parent, void *child); 32 static ngx_int_t ngx_http_auth_basic_init(ngx_conf_t *cf); 33 static char *ngx_http_auth_basic_user_file(ngx_conf_t *cf, ngx_command_t *cmd, 34 void *conf); 35 36 37 static ngx_command_t ngx_http_auth_basic_commands[] = { 38 39 { ngx_string("auth_basic"), 40 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF 41 |NGX_CONF_TAKE1, 42 ngx_http_set_complex_value_slot, 43 NGX_HTTP_LOC_CONF_OFFSET, 44 offsetof(ngx_http_auth_basic_loc_conf_t, realm), 45 NULL }, 46 47 { ngx_string("auth_basic_user_file"), 48 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF 49 |NGX_CONF_TAKE1, 50 ngx_http_auth_basic_user_file, 51 NGX_HTTP_LOC_CONF_OFFSET, 52 offsetof(ngx_http_auth_basic_loc_conf_t, user_file), 53 NULL }, 54 55 ngx_null_command 56 }; 57 58 59 static ngx_http_module_t ngx_http_auth_basic_module_ctx = { 60 NULL, /* preconfiguration */ 61 ngx_http_auth_basic_init, /* postconfiguration */ 62 63 NULL, /* create main configuration */ 64 NULL, /* init main configuration */ 65 66 NULL, /* create server configuration */ 67 NULL, /* merge server configuration */ 68 69 ngx_http_auth_basic_create_loc_conf, /* create location configuration */ 70 ngx_http_auth_basic_merge_loc_conf /* merge location configuration */ 71 }; 72 73 74 ngx_module_t ngx_http_auth_basic_module = { 75 NGX_MODULE_V1, 76 &ngx_http_auth_basic_module_ctx, /* module context */ 77 ngx_http_auth_basic_commands, /* module directives */ 78 NGX_HTTP_MODULE, /* module type */ 79 NULL, /* init master */ 80 NULL, /* init module */ 81 NULL, /* init process */ 82 NULL, /* init thread */ 83 NULL, /* exit thread */ 84 NULL, /* exit process */ 85 NULL, /* exit master */ 86 NGX_MODULE_V1_PADDING 87 }; 88 89 90 static ngx_int_t 91 ngx_http_auth_basic_handler(ngx_http_request_t *r) 92 { 93 off_t offset; 94 ssize_t n; 95 ngx_fd_t fd; 96 ngx_int_t rc; 97 ngx_err_t err; 98 ngx_str_t pwd, realm, user_file; 99 ngx_uint_t i, level, login, left, passwd; 100 ngx_file_t file; 101 ngx_http_auth_basic_loc_conf_t *alcf; 102 u_char buf[NGX_HTTP_AUTH_BUF_SIZE]; 103 enum { 104 sw_login, 105 sw_passwd, 106 sw_skip 107 } state; 108 109 alcf = ngx_http_get_module_loc_conf(r, ngx_http_auth_basic_module); 110 111 if (alcf->realm == NULL || alcf->user_file.value.data == NULL) { 112 return NGX_DECLINED; 113 } 114 115 if (ngx_http_complex_value(r, alcf->realm, &realm) != NGX_OK) { 116 return NGX_ERROR; 117 } 118 119 if (realm.len == 3 && ngx_strncmp(realm.data, "off", 3) == 0) { 120 return NGX_DECLINED; 121 } 122 123 rc = ngx_http_auth_basic_user(r); 124 125 if (rc == NGX_DECLINED) { 126 127 ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, 128 "no user/password was provided for basic authentication"); 129 130 return ngx_http_auth_basic_set_realm(r, &realm); 131 } 132 133 if (rc == NGX_ERROR) { 134 return NGX_HTTP_INTERNAL_SERVER_ERROR; 135 } 136 137 if (ngx_http_complex_value(r, &alcf->user_file, &user_file) != NGX_OK) { 138 return NGX_ERROR; 139 } 140 141 fd = ngx_open_file(user_file.data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0); 142 143 if (fd == NGX_INVALID_FILE) { 144 err = ngx_errno; 145 146 if (err == NGX_ENOENT) { 147 level = NGX_LOG_ERR; 148 rc = NGX_HTTP_FORBIDDEN; 149 150 } else { 151 level = NGX_LOG_CRIT; 152 rc = NGX_HTTP_INTERNAL_SERVER_ERROR; 153 } 154 155 ngx_log_error(level, r->connection->log, err, 156 ngx_open_file_n " \"%s\" failed", user_file.data); 157 158 return rc; 159 } 160 161 ngx_memzero(&file, sizeof(ngx_file_t)); 162 163 file.fd = fd; 164 file.name = user_file; 165 file.log = r->connection->log; 166 167 state = sw_login; 168 passwd = 0; 169 login = 0; 170 left = 0; 171 offset = 0; 172 173 for ( ;; ) { 174 i = left; 175 176 n = ngx_read_file(&file, buf + left, NGX_HTTP_AUTH_BUF_SIZE - left, 177 offset); 178 179 if (n == NGX_ERROR) { 180 ngx_http_auth_basic_close(&file); 181 return NGX_HTTP_INTERNAL_SERVER_ERROR; 182 } 183 184 if (n == 0) { 185 break; 186 } 187 188 for (i = left; i < left + n; i++) { 189 switch (state) { 190 191 case sw_login: 192 if (login == 0) { 193 194 if (buf[i] == '#' || buf[i] == CR) { 195 state = sw_skip; 196 break; 197 } 198 199 if (buf[i] == LF) { 200 break; 201 } 202 } 203 204 if (buf[i] != r->headers_in.user.data[login]) { 205 state = sw_skip; 206 break; 207 } 208 209 if (login == r->headers_in.user.len) { 210 state = sw_passwd; 211 passwd = i + 1; 212 } 213 214 login++; 215 216 break; 217 218 case sw_passwd: 219 if (buf[i] == LF || buf[i] == CR || buf[i] == ':') { 220 buf[i] = '\0'; 221 222 ngx_http_auth_basic_close(&file); 223 224 pwd.len = i - passwd; 225 pwd.data = &buf[passwd]; 226 227 return ngx_http_auth_basic_crypt_handler(r, &pwd, &realm); 228 } 229 230 break; 231 232 case sw_skip: 233 if (buf[i] == LF) { 234 state = sw_login; 235 login = 0; 236 } 237 238 break; 239 } 240 } 241 242 if (state == sw_passwd) { 243 left = left + n - passwd; 244 ngx_memmove(buf, &buf[passwd], left); 245 passwd = 0; 246 247 } else { 248 left = 0; 249 } 250 251 offset += n; 252 } 253 254 ngx_http_auth_basic_close(&file); 255 256 if (state == sw_passwd) { 257 pwd.len = i - passwd; 258 pwd.data = ngx_pnalloc(r->pool, pwd.len + 1); 259 if (pwd.data == NULL) { 260 return NGX_HTTP_INTERNAL_SERVER_ERROR; 261 } 262 263 ngx_cpystrn(pwd.data, &buf[passwd], pwd.len + 1); 264 265 return ngx_http_auth_basic_crypt_handler(r, &pwd, &realm); 266 } 267 268 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 269 "user \"%V\" was not found in \"%s\"", 270 &r->headers_in.user, user_file.data); 271 272 return ngx_http_auth_basic_set_realm(r, &realm); 273 } 274 275 276 static ngx_int_t 277 ngx_http_auth_basic_crypt_handler(ngx_http_request_t *r, ngx_str_t *passwd, 278 ngx_str_t *realm) 279 { 280 ngx_int_t rc; 281 u_char *encrypted; 282 283 rc = ngx_crypt(r->pool, r->headers_in.passwd.data, passwd->data, 284 &encrypted); 285 286 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 287 "rc: %i user: \"%V\" salt: \"%s\"", 288 rc, &r->headers_in.user, passwd->data); 289 290 if (rc != NGX_OK) { 291 return NGX_HTTP_INTERNAL_SERVER_ERROR; 292 } 293 294 if (ngx_strcmp(encrypted, passwd->data) == 0) { 295 return NGX_OK; 296 } 297 298 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 299 "encrypted: \"%s\"", encrypted); 300 301 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 302 "user \"%V\": password mismatch", 303 &r->headers_in.user); 304 305 return ngx_http_auth_basic_set_realm(r, realm); 306 } 307 308 309 static ngx_int_t 310 ngx_http_auth_basic_set_realm(ngx_http_request_t *r, ngx_str_t *realm) 311 { 312 size_t len; 313 u_char *basic, *p; 314 315 r->headers_out.www_authenticate = ngx_list_push(&r->headers_out.headers); 316 if (r->headers_out.www_authenticate == NULL) { 317 return NGX_HTTP_INTERNAL_SERVER_ERROR; 318 } 319 320 len = sizeof("Basic realm=\"\"") - 1 + realm->len; 321 322 basic = ngx_pnalloc(r->pool, len); 323 if (basic == NULL) { 324 r->headers_out.www_authenticate->hash = 0; 325 r->headers_out.www_authenticate = NULL; 326 return NGX_HTTP_INTERNAL_SERVER_ERROR; 327 } 328 329 p = ngx_cpymem(basic, "Basic realm=\"", sizeof("Basic realm=\"") - 1); 330 p = ngx_cpymem(p, realm->data, realm->len); 331 *p = '"'; 332 333 r->headers_out.www_authenticate->hash = 1; 334 ngx_str_set(&r->headers_out.www_authenticate->key, "WWW-Authenticate"); 335 r->headers_out.www_authenticate->value.data = basic; 336 r->headers_out.www_authenticate->value.len = len; 337 338 return NGX_HTTP_UNAUTHORIZED; 339 } 340 341 static void 342 ngx_http_auth_basic_close(ngx_file_t *file) 343 { 344 if (ngx_close_file(file->fd) == NGX_FILE_ERROR) { 345 ngx_log_error(NGX_LOG_ALERT, file->log, ngx_errno, 346 ngx_close_file_n " \"%s\" failed", file->name.data); 347 } 348 } 349 350 351 static void * 352 ngx_http_auth_basic_create_loc_conf(ngx_conf_t *cf) 353 { 354 ngx_http_auth_basic_loc_conf_t *conf; 355 356 conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_auth_basic_loc_conf_t)); 357 if (conf == NULL) { 358 return NULL; 359 } 360 361 return conf; 362 } 363 364 365 static char * 366 ngx_http_auth_basic_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) 367 { 368 ngx_http_auth_basic_loc_conf_t *prev = parent; 369 ngx_http_auth_basic_loc_conf_t *conf = child; 370 371 if (conf->realm == NULL) { 372 conf->realm = prev->realm; 373 } 374 375 if (conf->user_file.value.data == NULL) { 376 conf->user_file = prev->user_file; 377 } 378 379 return NGX_CONF_OK; 380 } 381 382 383 static ngx_int_t 384 ngx_http_auth_basic_init(ngx_conf_t *cf) 385 { 386 ngx_http_handler_pt *h; 387 ngx_http_core_main_conf_t *cmcf; 388 389 cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); 390 391 h = ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers); 392 if (h == NULL) { 393 return NGX_ERROR; 394 } 395 396 *h = ngx_http_auth_basic_handler; 397 398 return NGX_OK; 399 } 400 401 402 static char * 403 ngx_http_auth_basic_user_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 404 { 405 ngx_http_auth_basic_loc_conf_t *alcf = conf; 406 407 ngx_str_t *value; 408 ngx_http_compile_complex_value_t ccv; 409 410 if (alcf->user_file.value.data) { 411 return "is duplicate"; 412 } 413 414 value = cf->args->elts; 415 416 ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); 417 418 ccv.cf = cf; 419 ccv.value = &value[1]; 420 ccv.complex_value = &alcf->user_file; 421 ccv.zero = 1; 422 ccv.conf_prefix = 1; 423 424 if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { 425 return NGX_CONF_ERROR; 426 } 427 428 return NGX_CONF_OK; 429 } 430