ksgi
/
auth.c
264 строки · 6.6 Кб
1/* $Id$ */
2/*
3* Copyright (c) 2015--2018 Kristaps Dzonsons <kristaps@bsd.lv>
4* Copyright (c) 2018 Charles Collicutt <charles@collicutt.co.uk>
5*
6* Permission to use, copy, modify, and distribute this software for any
7* purpose with or without fee is hereby granted, provided that the above
8* copyright notice and this permission notice appear in all copies.
9*
10* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18#include "config.h"19
20#include <inttypes.h>21#if HAVE_MD522# include <sys/types.h>23# include <md5.h>24#endif25#include <stdarg.h>26#include <stdio.h>27#include <stdint.h>28#include <stdlib.h>29#include <string.h>30
31#include "kcgi.h"32#include "extern.h"33
34#define MD5Updatec(_ctx, _b, _sz) \35MD5Update((_ctx), (const uint8_t *)(_b), (_sz))36
37static const char b64[] =38"ABCDEFGHIJKLMNOPQRSTUVWXYZ"39"abcdefghijklmnopqrstuvwxyz"40"0123456789+/";41
42static size_t43base64len(size_t len)44{
45
46return((len + 2) / 3 * 4) + 1;47}
48
49static size_t50base64buf(char *enc, const char *str, size_t len)51{
52size_t i;53char *p;54
55p = enc;56
57for (i = 0; i < len - 2; i += 3) {58*p++ = b64[(str[i] >> 2) & 0x3F];59*p++ = b64[((str[i] & 0x3) << 4) |60((int)(str[i + 1] & 0xF0) >> 4)];61*p++ = b64[((str[i + 1] & 0xF) << 2) |62((int)(str[i + 2] & 0xC0) >> 6)];63*p++ = b64[str[i + 2] & 0x3F];64}65
66if (i < len) {67*p++ = b64[(str[i] >> 2) & 0x3F];68if (i == (len - 1)) {69*p++ = b64[((str[i] & 0x3) << 4)];70*p++ = '=';71} else {72*p++ = b64[((str[i] & 0x3) << 4) |73((int)(str[i + 1] & 0xF0) >> 4)];74*p++ = b64[((str[i + 1] & 0xF) << 2)];75}76*p++ = '=';77}78
79*p++ = '\0';80return (p - enc);81}
82
83int
84khttpbasic_validate(const struct kreq *req,85const char *user, const char *pass)86{
87char *buf, *enc;88size_t sz;89int rc;90
91if (req->rawauth.type != KAUTH_BASIC &&92req->rawauth.type != KAUTH_BEARER)93return (-1);94else if (req->method == KMETHOD__MAX)95return (-1);96else if (req->rawauth.authorised == 0)97return (-1);98
99/* Make sure we don't bail on memory allocation. */100
101sz = strlen(user) + 1 + strlen(pass) + 1;102if ((buf = kxmalloc(sz)) == NULL)103return (-1);104
105sz = snprintf(buf, sz, "%s:%s", user, pass);106if ((enc = kxmalloc(base64len(sz))) == NULL) {107free(buf);108return (-1);109}110
111base64buf(enc, buf, sz);112rc = strcmp(enc, req->rawauth.d.basic.response) == 0;113
114free(enc);115free(buf);116return rc;117}
118
119int
120khttpdigest_validatehash(const struct kreq *req, const char *skey4)121{
122MD5_CTX ctx;123unsigned char ha1[MD5_DIGEST_LENGTH],124ha2[MD5_DIGEST_LENGTH],125ha3[MD5_DIGEST_LENGTH];126char skey1[MD5_DIGEST_LENGTH * 2 + 1],127skey2[MD5_DIGEST_LENGTH * 2 + 1],128skey3[MD5_DIGEST_LENGTH * 2 + 1],129skeyb[MD5_DIGEST_LENGTH * 2 + 1],130count[9];131size_t i;132const struct khttpdigest *auth;133
134/*135* Make sure we're a digest with all fields intact.
136*/
137if (KAUTH_DIGEST != req->rawauth.type)138return(-1);139else if (KMETHOD__MAX == req->method)140return(-1);141else if (0 == req->rawauth.authorised)142return(-1);143
144auth = &req->rawauth.d.digest;145
146/*147* MD5-sess hashes the nonce and client nonce as well as the
148* existing hash (user/real/pass).
149*/
150
151if (KHTTPALG_MD5_SESS == auth->alg) {152MD5Init(&ctx);153MD5Updatec(&ctx, skey4, strlen(skey4));154MD5Updatec(&ctx, ":", 1);155MD5Updatec(&ctx, auth->nonce, strlen(auth->nonce));156MD5Updatec(&ctx, ":", 1);157MD5Updatec(&ctx, auth->cnonce, strlen(auth->cnonce));158MD5Final(ha1, &ctx);159for (i = 0; i < MD5_DIGEST_LENGTH; i++)160snprintf(&skey1[i * 2], 3, "%02x", ha1[i]);161} else162strlcpy(skey1, skey4, sizeof(skey1));163
164/* Now start the "auth" hash sequence. */165
166MD5Init(&ctx);167MD5Updatec(&ctx, kmethods[req->method],168strlen(kmethods[req->method]));169MD5Updatec(&ctx, ":", 1);170MD5Updatec(&ctx, auth->uri, strlen(auth->uri));171
172/*173* If we're requesting integrity authentication ("auth-int"),
174* then we also bring in the hash of the message body.
175*/
176
177if (KHTTPQOP_AUTH_INT == auth->qop) {178/* This shouldn't happen... */179if (NULL == req->rawauth.digest)180return(-1);181
182for (i = 0; i < MD5_DIGEST_LENGTH; i++)183snprintf(&skeyb[i * 2], 3, "%02x",184(unsigned char)req->rawauth.digest[i]);185
186MD5Updatec(&ctx, ":", 1);187MD5Updatec(&ctx, skeyb, MD5_DIGEST_LENGTH * 2);188}189
190MD5Final(ha2, &ctx);191
192for (i = 0; i < MD5_DIGEST_LENGTH; i++)193snprintf(&skey2[i * 2], 3, "%02x", ha2[i]);194
195if (KHTTPQOP_AUTH_INT == auth->qop ||196KHTTPQOP_AUTH == auth->qop) {197snprintf(count, sizeof(count), "%08" PRIx32, auth->count);198MD5Init(&ctx);199MD5Updatec(&ctx, skey1, MD5_DIGEST_LENGTH * 2);200MD5Updatec(&ctx, ":", 1);201MD5Updatec(&ctx, auth->nonce, strlen(auth->nonce));202MD5Updatec(&ctx, ":", 1);203MD5Updatec(&ctx, count, strlen(count));204MD5Updatec(&ctx, ":", 1);205MD5Updatec(&ctx, auth->cnonce, strlen(auth->cnonce));206MD5Updatec(&ctx, ":", 1);207if (KHTTPQOP_AUTH_INT == auth->qop)208MD5Updatec(&ctx, "auth-int", 8);209else210MD5Updatec(&ctx, "auth", 4);211MD5Updatec(&ctx, ":", 1);212MD5Updatec(&ctx, skey2, MD5_DIGEST_LENGTH * 2);213MD5Final(ha3, &ctx);214} else {215MD5Init(&ctx);216MD5Updatec(&ctx, skey1, MD5_DIGEST_LENGTH * 2);217MD5Updatec(&ctx, ":", 1);218MD5Updatec(&ctx, auth->nonce, strlen(auth->nonce));219MD5Updatec(&ctx, ":", 1);220MD5Updatec(&ctx, skey2, MD5_DIGEST_LENGTH * 2);221MD5Final(ha3, &ctx);222}223
224for (i = 0; i < MD5_DIGEST_LENGTH; i++)225snprintf(&skey3[i * 2], 3, "%02x", ha3[i]);226
227return(0 == strcmp(auth->response, skey3));228}
229
230int
231khttpdigest_validate(const struct kreq *req, const char *pass)232{
233MD5_CTX ctx;234unsigned char ha4[MD5_DIGEST_LENGTH];235char skey4[MD5_DIGEST_LENGTH * 2 + 1];236size_t i;237const struct khttpdigest *auth;238
239/*240* Make sure we're a digest with all fields intact.
241*/
242
243if (KAUTH_DIGEST != req->rawauth.type)244return(-1);245else if (KMETHOD__MAX == req->method)246return(-1);247else if (0 == req->rawauth.authorised)248return(-1);249
250auth = &req->rawauth.d.digest;251
252MD5Init(&ctx);253MD5Updatec(&ctx, auth->user, strlen(auth->user));254MD5Updatec(&ctx, ":", 1);255MD5Updatec(&ctx, auth->realm, strlen(auth->realm));256MD5Updatec(&ctx, ":", 1);257MD5Updatec(&ctx, pass, strlen(pass));258MD5Final(ha4, &ctx);259
260for (i = 0; i < MD5_DIGEST_LENGTH; i++)261snprintf(&skey4[i * 2], 3, "%02x", ha4[i]);262
263return(khttpdigest_validatehash(req, skey4));264}
265