mirror of
https://github.com/darkk/redsocks.git
synced 2025-08-25 11:15:30 +00:00
implement http-auth for basic and digest scheme
This commit is contained in:
parent
3655d75040
commit
35a8f066ec
2
Makefile
2
Makefile
@ -3,7 +3,7 @@ CFLAGS=-std=gnu99 -Wall -g -O0
|
||||
.PHONY: all
|
||||
all: redsocks
|
||||
|
||||
obj = parser.o main.o redsocks.o log.o http-connect.o socks4.o socks5.o http-relay.o base.o base64.o md5.o
|
||||
obj = parser.o main.o redsocks.o log.o http-connect.o socks4.o socks5.o http-relay.o base.o base64.o md5.o http-auth.o
|
||||
src = $(patsubst %.o,%.c,$(obj))
|
||||
|
||||
redsocks: $(obj)
|
||||
|
285
http-auth.c
Normal file
285
http-auth.c
Normal file
@ -0,0 +1,285 @@
|
||||
/* redsocks - transparent TCP-to-proxy redirector
|
||||
* Copyright (C) 2007-2008 Leonid Evdokimov <leon@darkk.net.ru>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* http-auth library, provide basic and digest scheme
|
||||
* see RFC 2617 for details
|
||||
*
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "utils.h"
|
||||
#include "md5.h"
|
||||
#include "base64.h"
|
||||
|
||||
#include "http-auth.h"
|
||||
|
||||
int strncmp_nocase(const char *a, const char *b, int num)
|
||||
{
|
||||
if (!a || !b) {
|
||||
if (!a && !b)
|
||||
return 0;
|
||||
return !a ? 1 : -1;
|
||||
}
|
||||
while (num --) {
|
||||
if (!*a || !*b) {
|
||||
if (!*a && !*b)
|
||||
return 0;
|
||||
return !*a ? 1 : -1;
|
||||
}
|
||||
if (tolower(*a) != tolower(*b))
|
||||
return (int)tolower(*a) - (int)tolower(*b);
|
||||
a ++;
|
||||
b ++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
char* basic_authentication_encode(const char *user, const char *passwd)
|
||||
{
|
||||
/* prepare the user:pass key pair */
|
||||
int pair_len = strlen(user) + 1 + strlen(passwd);
|
||||
char *pair_ptr = (char*)malloc(pair_len + 1);
|
||||
|
||||
sprintf(pair_ptr, "%s:%s", user, passwd);
|
||||
|
||||
/* calculate the final string length */
|
||||
int basic_len = 6 + BASE64_SIZE(pair_len);
|
||||
char *basic_ptr = (char*)malloc(basic_len + 1);
|
||||
|
||||
strcpy(basic_ptr, "Basic ");
|
||||
|
||||
if (!base64_encode(basic_ptr + 6, basic_len - 6, (const uint8_t*)pair_ptr, pair_len))
|
||||
return NULL;
|
||||
|
||||
return basic_ptr;
|
||||
}
|
||||
|
||||
#define MD5_HASHLEN (16)
|
||||
|
||||
static void dump_hash(char *buf, const unsigned char *hash)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MD5_HASHLEN; i++) {
|
||||
buf += sprintf(buf, "%02x", hash[i]);
|
||||
}
|
||||
|
||||
*buf = 0;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
const char *b, *e;
|
||||
} param_token;
|
||||
|
||||
/* Extract a parameter from the string (typically an HTTP header) at
|
||||
**SOURCE and advance SOURCE to the next parameter. Return false
|
||||
when there are no more parameters to extract. The name of the
|
||||
parameter is returned in NAME, and the value in VALUE. If the
|
||||
parameter has no value, the token's b==e.*/
|
||||
|
||||
static int extract_param(const char **source, param_token *name, param_token *value, char separator)
|
||||
{
|
||||
const char *p = *source;
|
||||
|
||||
while (isspace (*p))
|
||||
++p;
|
||||
|
||||
if (!*p)
|
||||
{
|
||||
*source = p;
|
||||
return 0; /* no error; nothing more to extract */
|
||||
}
|
||||
|
||||
/* Extract name. */
|
||||
name->b = p;
|
||||
while (*p && !isspace (*p) && *p != '=' && *p != separator)
|
||||
++p;
|
||||
name->e = p;
|
||||
if (name->b == name->e)
|
||||
return 0; /* empty name: error */
|
||||
|
||||
while (isspace (*p))
|
||||
++p;
|
||||
if (*p == separator || !*p) /* no value */
|
||||
{
|
||||
value->b = value->e = "";
|
||||
if (*p == separator) ++p;
|
||||
*source = p;
|
||||
return 1;
|
||||
}
|
||||
if (*p != '=')
|
||||
return 0; /* error */
|
||||
|
||||
/* *p is '=', extract value */
|
||||
++p;
|
||||
while (isspace (*p)) ++p;
|
||||
if (*p == '"') /* quoted */
|
||||
{
|
||||
value->b = ++p;
|
||||
while (*p && *p != '"') ++p;
|
||||
if (!*p)
|
||||
return 0;
|
||||
value->e = p++;
|
||||
/* Currently at closing quote; find the end of param. */
|
||||
while (isspace (*p)) ++p;
|
||||
while (*p && *p != separator) ++p;
|
||||
if (*p == separator)
|
||||
++p;
|
||||
else if (*p)
|
||||
/* garbage after closed quote, e.g. foo="bar"baz */
|
||||
return 0;
|
||||
}
|
||||
else /* unquoted */
|
||||
{
|
||||
value->b = p;
|
||||
while (*p && *p != separator) ++p;
|
||||
value->e = p;
|
||||
while (value->e != value->b && isspace (value->e[-1]))
|
||||
--value->e;
|
||||
if (*p == separator) ++p;
|
||||
}
|
||||
*source = p;
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
char* digest_authentication_encode(const char *line, const char *user, const char *passwd, const char *method, const char *path, int count, const char *cnonce)
|
||||
{
|
||||
char *realm = NULL, *opaque = NULL, *nonce = NULL, *qop = NULL;
|
||||
char nc[9];
|
||||
sprintf(nc, "%08x", count);
|
||||
|
||||
const char *ptr = line;
|
||||
param_token name, value;
|
||||
|
||||
while (extract_param(&ptr, &name, &value, ',')) {
|
||||
int namelen = name.e - name.b;
|
||||
int valuelen = value.e - value.b;
|
||||
|
||||
if (strncmp_nocase(name.b, "realm" , namelen) == 0)
|
||||
strncpy(realm = (char *)malloc(valuelen + 1), value.b, valuelen);
|
||||
if (strncmp_nocase(name.b, "opaque", namelen) == 0)
|
||||
strncpy(opaque = (char *)malloc(valuelen + 1), value.b, valuelen);
|
||||
if (strncmp_nocase(name.b, "nonce" , namelen) == 0)
|
||||
strncpy(nonce = (char *)malloc(valuelen + 1), value.b, valuelen);
|
||||
if (strncmp_nocase(name.b, "qop" , namelen) == 0)
|
||||
strncpy(qop = (char *)malloc(valuelen + 1), value.b, valuelen);
|
||||
}
|
||||
|
||||
if (!realm || !nonce || !user || !passwd || !path || !method) {
|
||||
free_null(realm);
|
||||
free_null(opaque);
|
||||
free_null(nonce);
|
||||
free_null(qop);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!qop && strncmp_nocase(qop, "auth", 4) != 0) {
|
||||
/* FIXME: currently don't support auth-int */
|
||||
free_null(qop);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* calculate the digest value */
|
||||
md5_state_t ctx;
|
||||
md5_byte_t hash[MD5_HASHLEN];
|
||||
char a1buf[MD5_HASHLEN * 2 + 1], a2buf[MD5_HASHLEN * 2 + 1];
|
||||
char response[MD5_HASHLEN * 2 + 1];
|
||||
|
||||
/* A1 = username-value ":" realm-value ":" passwd */
|
||||
md5_init(&ctx);
|
||||
md5_append(&ctx, (md5_byte_t*)user, strlen(user));
|
||||
md5_append(&ctx, (md5_byte_t*)":", 1);
|
||||
md5_append(&ctx, (md5_byte_t*)realm, strlen(realm));
|
||||
md5_append(&ctx, (md5_byte_t*)":", 1);
|
||||
md5_append(&ctx, (md5_byte_t*)passwd, strlen(passwd));
|
||||
md5_finish(&ctx, hash);
|
||||
dump_hash(a1buf, hash);
|
||||
|
||||
/* A2 = Method ":" digest-uri-value */
|
||||
md5_init(&ctx);
|
||||
md5_append(&ctx, (md5_byte_t*)method, strlen(method));
|
||||
md5_append(&ctx, (md5_byte_t*)":", 1);
|
||||
md5_append(&ctx, (md5_byte_t*)path, strlen(path));
|
||||
md5_finish(&ctx, hash);
|
||||
dump_hash(a2buf, hash);
|
||||
|
||||
/* qop set: request-digest = H(A1) ":" nonce-value ":" nc-value ":" cnonce-value ":" qop-value ":" H(A2) */
|
||||
/* not set: request-digest = H(A1) ":" nonce-value ":" H(A2) */
|
||||
md5_init(&ctx);
|
||||
md5_append(&ctx, (md5_byte_t*)a1buf, strlen(a1buf));
|
||||
md5_append(&ctx, (md5_byte_t*)":", 1);
|
||||
md5_append(&ctx, (md5_byte_t*)nonce, strlen(nonce));
|
||||
md5_append(&ctx, (md5_byte_t*)":", 1);
|
||||
if (qop) {
|
||||
md5_append(&ctx, (md5_byte_t*)nc, strlen(nc));
|
||||
md5_append(&ctx, (md5_byte_t*)":", 1);
|
||||
md5_append(&ctx, (md5_byte_t*)cnonce, strlen(cnonce));
|
||||
md5_append(&ctx, (md5_byte_t*)":", 1);
|
||||
md5_append(&ctx, (md5_byte_t*)qop, strlen(qop));
|
||||
md5_append(&ctx, (md5_byte_t*)":", 1);
|
||||
}
|
||||
md5_append(&ctx, (md5_byte_t*)a2buf, strlen(a2buf));
|
||||
md5_finish(&ctx, hash);
|
||||
dump_hash(response, hash);
|
||||
|
||||
/* prepare the final string */
|
||||
int len = 256;
|
||||
//if (!realm || !nonce || !user || !passwd || !path || !method)
|
||||
len += strlen(user);
|
||||
len += strlen(realm);
|
||||
len += strlen(nonce);
|
||||
len += strlen(path);
|
||||
len += strlen(response);
|
||||
|
||||
if (qop) {
|
||||
len += strlen(qop);
|
||||
len += strlen(nc);
|
||||
len += strlen(cnonce);
|
||||
}
|
||||
|
||||
if (opaque) {
|
||||
len += strlen(opaque);
|
||||
}
|
||||
|
||||
char *res = (char*)malloc(len);
|
||||
if (!qop) {
|
||||
sprintf(res, "username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\"",
|
||||
user, realm, nonce, path, response);
|
||||
} else {
|
||||
sprintf(res, "username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\", qop=%s, nc=%s, cnonce=\"%s\"",
|
||||
user, realm, nonce, path, response, qop, nc, cnonce);
|
||||
}
|
||||
|
||||
if (opaque) {
|
||||
char *p = res + strlen(res);
|
||||
strcat (p, ", opaque=\"");
|
||||
strcat (p, opaque);
|
||||
strcat (p, "\"");
|
||||
}
|
||||
|
||||
free_null(realm);
|
||||
free_null(opaque);
|
||||
free_null(nonce);
|
||||
free_null(qop);
|
||||
return res;
|
||||
}
|
24
http-auth.h
Normal file
24
http-auth.h
Normal file
@ -0,0 +1,24 @@
|
||||
#ifndef HTTP_AUTH_H
|
||||
#define HTTP_AUTH_H
|
||||
|
||||
|
||||
int strncmp_nocase(const char *a, const char *b, int num);
|
||||
|
||||
|
||||
/*
|
||||
* Create the authentication header contents for the `Basic' scheme.
|
||||
* This is done by encoding the string "USER:PASS" to base64 and
|
||||
* prepending the string "Basic " in front of it.
|
||||
*
|
||||
*/
|
||||
|
||||
char* basic_authentication_encode(const char *user, const char *passwd);
|
||||
|
||||
/*
|
||||
* Create the authentication header contents for the 'Digest' scheme.
|
||||
* only md5 method is available, see RFC 2617 for detail.
|
||||
*
|
||||
*/
|
||||
char* digest_authentication_encode(const char *line, const char *user, const char *passwd, const char *method, const char *path, int count, const char *cnonce);
|
||||
|
||||
#endif /* HTTP_AUTH_H */
|
1
utils.h
1
utils.h
@ -20,6 +20,7 @@
|
||||
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
|
||||
(type *)( (char *)__mptr - offsetof(type,member) );})
|
||||
|
||||
#define free_null(p) if (!(p)) ; else free (p)
|
||||
|
||||
/* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */
|
||||
/* vim:set foldmethod=marker foldlevel=32 foldmarker={,}: */
|
||||
|
Loading…
Reference in New Issue
Block a user