Digest Auth: part one

This commit is contained in:
Evgeny Grin (Karlson2k)
2024-12-22 19:09:53 +01:00
parent de082e34b2
commit 6a1a29c795
14 changed files with 1316 additions and 94 deletions
+10 -10
View File
@@ -5635,18 +5635,18 @@ AS_CASE([${enable_md5}],
)
AS_IF([test "x${enable_md5}" = "xbuiltin" || test "x${enable_md5}" = "xtlslib" ],
[
AC_DEFINE([[MHD_MD5_SUPPORT]],[[1]],
AC_DEFINE([[MHD_SUPPORT_MD5]],[[1]],
[Define to 1 if libmicrohttpd is compiled with MD5 hashing support.])
]
)
AS_IF([test "x${enable_md5}" = "xtlslib" ],
[
AC_DEFINE([[MHD_MD5_TLSLIB]],[[1]],
AC_DEFINE([[MHD_MD5_EXTR]],[[1]],
[Define to 1 if libmicrohttpd is compiled with MD5 hashing by TLS library.])
]
)
AM_CONDITIONAL([ENABLE_MD5], [[test "x${enable_md5}" = "xbuiltin" || test "x${enable_md5}" = "xtlslib" ]])
AM_CONDITIONAL([ENABLE_MD5_EXT], [[test "x${enable_md5}" = "xtlslib" ]])
AM_CONDITIONAL([MHD_SUPPORT_MD5], [[test "x${enable_md5}" = "xbuiltin" || test "x${enable_md5}" = "xtlslib" ]])
AM_CONDITIONAL([MHD_MD5_EXTR], [[test "x${enable_md5}" = "xtlslib" ]])
AC_MSG_RESULT([[${enable_md5_MSG}]])
# optional: SHA-256 support for Digest Auth. Enabled by default.
@@ -5757,18 +5757,18 @@ AS_CASE([${enable_sha256}],
)
AS_IF([test "x${enable_sha256}" = "xbuiltin" || test "x${enable_sha256}" = "xtlslib" ],
[
AC_DEFINE([[MHD_SHA256_SUPPORT]],[[1]],
AC_DEFINE([[MHD_SUPPORT_SHA256]],[[1]],
[Define to 1 if libmicrohttpd is compiled with SHA-256 hashing support.])
]
)
AS_IF([test "x${enable_sha256}" = "xtlslib" ],
[
AC_DEFINE([[MHD_SHA256_TLSLIB]],[[1]],
AC_DEFINE([[MHD_SHA256_EXTR]],[[1]],
[Define to 1 if libmicrohttpd is compiled with SHA-256 hashing by TLS library.])
]
)
AM_CONDITIONAL([ENABLE_SHA256], [[test "x${enable_sha256}" = "xbuiltin" || test "x${enable_sha256}" = "xtlslib" ]])
AM_CONDITIONAL([ENABLE_SHA256_EXT], [[test "x${enable_sha256}" = "xtlslib" ]])
AM_CONDITIONAL([MHD_SUPPORT_SHA256], [[test "x${enable_sha256}" = "xbuiltin" || test "x${enable_sha256}" = "xtlslib" ]])
AM_CONDITIONAL([MHD_SHA256_EXTR], [[test "x${enable_sha256}" = "xtlslib" ]])
AC_MSG_RESULT([[${enable_sha256_MSG}]])
# optional: SHA-512/256 support for Digest Auth. Enabled by default.
@@ -5797,11 +5797,11 @@ AS_CASE([${enable_sha512_256}],
)
AS_VAR_IF([[enable_sha512_256]],[["yes"]],
[
AC_DEFINE([[MHD_SHA512_256_SUPPORT]],[[1]],
AC_DEFINE([[MHD_SUPPORT_SHA512_256]],[[1]],
[Define to 1 if libmicrohttpd is compiled with SHA-512/256 hashing support.])
]
)
AM_CONDITIONAL([ENABLE_SHA512_256], [[test "x${enable_sha512_256}" = "xyes"]])
AM_CONDITIONAL([MHD_SUPPORT_SHA512_256], [[test "x${enable_sha512_256}" = "xyes"]])
AC_MSG_RESULT([[${enable_sha512_256_MSG}]])
AS_IF([test "x$enable_digest_auth" != "xno"],
+291 -35
View File
@@ -1621,6 +1621,16 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
* cannot be provided due to some error or other reasons.
*/
MHD_SC_INFO_GET_TYPE_UNAVAILALBE = 60204
,
/**
* The type of the Digest Auth algorithm is unknown or not supported.
*/
MHD_SC_AUTH_DIGEST_ALGO_NOT_SUPPORTED = 60240
,
/**
* The the Digest Auth QOP value is unknown or not supported.
*/
MHD_SC_AUTH_DIGEST_QOP_NOT_SUPPORTED = 60241
};
/**
@@ -7907,20 +7917,32 @@ MHD_FN_PAR_CSTR_ (4);
/**
* Create an action to request authentication from the client
* Add Digest Authentication "challenge" to the response.
*
* If @a mqop allows both RFC 2069 (MHD_DIGEST_AUTH_QOP_NONE) and QOP with
* value, then response is formed like if MHD_DIGEST_AUTH_QOP_NONE bit was
* not set, because such response should be backward-compatible with RFC 2069.
* The response must have #MHD_HTTP_STATUS_UNAUTHORIZED status code.
*
* If @a mqop allows both RFC 2069 (#MHD_DIGEST_AUTH_QOP_NONE) and other QOP
* values, then the "challenge" is formed like if MHD_DIGEST_AUTH_QOP_NONE bit
* was not set, because such "challenge" should be backward-compatible with
* RFC 2069.
*
* If @a mqop allows only MHD_DIGEST_AUTH_MULT_QOP_NONE, then the response is
* formed in strict accordance with RFC 2069 (no 'qop', no 'userhash', no
* 'charset'). For better compatibility with clients, it is recommended (but
* not required) to set @a domain to NULL in this mode.
*
* At most one action can be created for any request.
* New nonces are generated each time when the resulting response is used.
*
* @param request the request
* See RFC 7616, section 3.3 for details.
*
* @param response the response to update; should contain the "access denied"
* body;
* note: this function sets the "WWW Authenticate" header and
* the caller should not set this header;
* the response must have #MHD_HTTP_STATUS_UNAUTHORIZED status
* code;
* the NULL is tolerated (the result is
* #MHD_SC_RESP_POINTER_NULL)
* @param realm the realm presented to the client
* @param opaque the string for opaque value, can be NULL, but NULL is
* not recommended for better compatibility with clients;
@@ -7935,24 +7957,17 @@ MHD_FN_PAR_CSTR_ (4);
* credentials could be used for any URI on the same host);
* this list provides information for the client only and does
* not actually restrict anything on the server side
* @param response the reply to send; should contain the "access denied"
* body;
* note: this function sets the "WWW Authenticate" header and
* the caller should not set this header;
* the response must have #MHD_HTTP_STATUS_FORBIDDEN status
* code, must not have #MHD_R_O_REUSABLE enabled;
* the NULL is tolerated (the result is NULL)
* @param signal_stale if set to #MHD_YES then indication of stale nonce used in
* the client's request is signalled by adding 'stale=true'
* to the authentication header, this instructs the client
* to retry immediately with the new nonce and the same
* credentials, without asking user for the new password
* @param indicate_stale if set to #MHD_YES then indication of stale nonce used
* in the client's request is indicated by adding
* 'stale=true' to the authentication header, this
* instructs the client to retry immediately with the new
* nonce and the same credentials, without asking user
* for the new password
* @param mqop the QOP to use
* @param algo digest algorithm to use; if several algorithms are allowed
* then MD5 is preferred (currently, may be changed in next
* versions)
* @param userhash_support if set to non-zero value (#MHD_YES) then support of
* userhash is indicated, allowing client to provide
* then one challenge for each allowed algorithm is added
* @param userhash_support if set to #MHD_YES then support of userhash is
* indicated, allowing client to provide
* hash("username:realm") instead of the username in
* clear text;
* note that clients are allowed to provide the username
@@ -7964,24 +7979,265 @@ MHD_FN_PAR_CSTR_ (4);
* @param prefer_utf8 if not set to #MHD_NO, parameter 'charset=UTF-8' is
* added, indicating for the client that UTF-8 encoding for
* the username is preferred
* @return pointer to the action on success,
* NULL on failure
* @return #MHD_SC_OK if succeed,
* #MHD_SC_TOO_LATE if the response has been already "frozen" (used to
* create an action),
* #MHD_SC_RESP_HEADERS_CONFLICT if Digest Authentication "challenge"
* has been added already,
* #MHD_SC_RESP_POINTER_NULL if @a response is NULL,
* #MHD_SC_RESP_HTTP_CODE_NOT_SUITABLE is response status code is wrong,
* #MHD_SC_RESP_HEADER_VALUE_INVALID if @a realm, @a opaque or @a domain
* have wrong characters or zero length (for @a realm),
* #MHD_SC_RESPONSE_HEADER_MEM_ALLOC_FAILED if memory allocation failed,
* or other error code if failed
* @ingroup authentication
*/
MHD_EXTERN_ const struct MHD_Action *
MHD_action_digest_auth_required_response (
struct MHD_Request *request,
const char *realm,
const char *opaque,
const char *domain,
struct MHD_Response *response,
enum MHD_Bool signal_stale,
MHD_EXTERN_ enum MHD_StatusCode
MHD_response_add_auth_digest_challenge (
struct MHD_Response *MHD_RESTRICT response,
const char *MHD_RESTRICT realm,
const char *MHD_RESTRICT opaque,
const char *MHD_RESTRICT domain,
enum MHD_Bool indicate_stale,
enum MHD_DigestAuthMultiQOP mqop,
enum MHD_DigestAuthMultiAlgo algo,
enum MHD_DigestAuthMultiAlgo malgo,
enum MHD_Bool userhash_support,
enum MHD_Bool prefer_utf8)
MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_CSTR_ (2)
MHD_FN_PAR_CSTR_ (3) MHD_FN_PAR_CSTR_ (4) MHD_FN_PAR_NONNULL_ (5);
MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_CSTR_ (2)
MHD_FN_PAR_CSTR_ (3) MHD_FN_PAR_CSTR_ (4);
#ifndef MHD_NO_STATIC_INLINE
/**
* Create action to reply with Digest Authentication "challenge".
*
* The @a response must have #MHD_HTTP_STATUS_UNAUTHORIZED status code.
*
* See RFC 7616, section 3.3 for details.
*
* @param request the request to create the action for
* @param realm the realm presented to the client
* @param opaque the string for opaque value, can be NULL, but NULL is
* not recommended for better compatibility with clients;
* the recommended format is hex or Base64 encoded string
* @param domain the optional space-separated list of URIs for which the
* same authorisation could be used, URIs can be in form
* "path-absolute" (the path for the same host with initial slash)
* or in form "absolute-URI" (the full path with protocol), in
* any case client may assume that URI is in the same "protection
* space" if it starts with any of values specified here;
* could be NULL (clients typically assume that the same
* credentials could be used for any URI on the same host);
* this list provides information for the client only and does
* not actually restrict anything on the server side
* @param indicate_stale if set to #MHD_YES then indication of stale nonce used
* in the client's request is indicated by adding
* 'stale=true' to the authentication header, this
* instructs the client to retry immediately with the new
* nonce and the same credentials, without asking user
* for the new password
* @param mqop the QOP to use
* @param algo digest algorithm to use; if several algorithms are allowed
* then one challenge for each allowed algorithm is added
* @param userhash_support if set to #MHD_YES then support of userhash is
* indicated, allowing client to provide
* hash("username:realm") instead of the username in
* clear text;
* note that clients are allowed to provide the username
* in cleartext even if this parameter set to non-zero;
* when userhash is used, application must be ready to
* identify users by provided userhash value instead of
* username; see #MHD_digest_auth_calc_userhash() and
* #MHD_digest_auth_calc_userhash_hex()
* @param prefer_utf8 if not set to #MHD_NO, parameter 'charset=UTF-8' is
* added, indicating for the client that UTF-8 encoding for
* the username is preferred
* @param response the response to update; should contain the "access denied"
* body;
* note: this function sets the "WWW Authenticate" header and
* the caller should not set this header;
* the response must have #MHD_HTTP_STATUS_UNAUTHORIZED status
* code;
* the NULL is tolerated (the result is
* #MHD_SC_RESP_POINTER_NULL)
* @param abort_if_failed if set to #MHD_NO the response will be used even if
* failed to add Basic Authentication "challenge",
* if not set to #MHD_NO the request will be aborted
* if the "challenge" could not be added.
* @return pointer to the action, the action must be consumed
* otherwise response object may leak;
* NULL if failed or if any action has been already created for
* the @a request;
* when failed the response object is consumed and need not
* to be "destroyed"
* @ingroup authentication
*/
MHD_STATIC_INLINE_
MHD_FN_PAR_NONNULL_ (1)
MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_CSTR_ (2)
const struct MHD_Action *
MHD_action_digest_auth_challenge (struct MHD_Request *MHD_RESTRICT request,
const char *MHD_RESTRICT realm,
const char *MHD_RESTRICT opaque,
const char *MHD_RESTRICT domain,
enum MHD_Bool indicate_stale,
enum MHD_DigestAuthMultiQOP mqop,
enum MHD_DigestAuthMultiAlgo malgo,
enum MHD_Bool userhash_support,
enum MHD_Bool prefer_utf8,
struct MHD_Response *MHD_RESTRICT response,
enum MHD_Bool abort_if_failed)
{
if ((MHD_SC_OK !=
MHD_response_add_auth_digest_challenge (response, realm, opaque, domain,
indicate_stale, mqop, malgo,
userhash_support, prefer_utf8))
&& (MHD_NO != abort_if_failed))
{
MHD_response_destroy (response);
return MHD_action_abort_request (request);
}
return MHD_action_from_response (request, response);
}
MHD_STATIC_INLINE_END_
/**
* Create action to reply with Digest Authentication "challenge".
*
* The @a response must have #MHD_HTTP_STATUS_UNAUTHORIZED status code.
*
* If the @a response object cannot be extended with the "challenge",
* the @a response is used to reply without the "challenge".
*
* @param request the request to create the action for
* @param realm the realm presented to the client
* @param opaque the string for opaque value, can be NULL, but NULL is
* not recommended for better compatibility with clients;
* the recommended format is hex or Base64 encoded string
* @param domain the optional space-separated list of URIs for which the
* same authorisation could be used, URIs can be in form
* "path-absolute" (the path for the same host with initial slash)
* or in form "absolute-URI" (the full path with protocol), in
* any case client may assume that URI is in the same "protection
* space" if it starts with any of values specified here;
* could be NULL (clients typically assume that the same
* credentials could be used for any URI on the same host);
* this list provides information for the client only and does
* not actually restrict anything on the server side
* @param indicate_stale if set to #MHD_YES then indication of stale nonce used
* in the client's request is indicated by adding
* 'stale=true' to the authentication header, this
* instructs the client to retry immediately with the new
* nonce and the same credentials, without asking user
* for the new password
* @param mqop the QOP to use
* @param algo digest algorithm to use; if several algorithms are allowed
* then one challenge for each allowed algorithm is added
* @param userhash_support if set to #MHD_YES then support of userhash is
* indicated, allowing client to provide
* hash("username:realm") instead of the username in
* clear text;
* note that clients are allowed to provide the username
* in cleartext even if this parameter set to non-zero;
* when userhash is used, application must be ready to
* identify users by provided userhash value instead of
* username; see #MHD_digest_auth_calc_userhash() and
* #MHD_digest_auth_calc_userhash_hex()
* @param prefer_utf8 if not set to #MHD_NO, parameter 'charset=UTF-8' is
* added, indicating for the client that UTF-8 encoding for
* the username is preferred
* @param response the response to update; should contain the "access denied"
* body;
* note: this function sets the "WWW Authenticate" header and
* the caller should not set this header;
* the response must have #MHD_HTTP_STATUS_UNAUTHORIZED status
* code;
* the NULL is tolerated (the result is
* #MHD_SC_RESP_POINTER_NULL)
* @return pointer to the action, the action must be consumed
* otherwise response object may leak;
* NULL if failed or if any action has been already created for
* the @a request;
* when failed the response object is consumed and need not
* to be "destroyed"
* @ingroup authentication
*/
#define MHD_action_digest_auth_challenge_p(rq,rlm,opq,dmn,stl,mqop,malgo, \
uh,utf,resp) \
MHD_action_digest_auth_challenge ((rq),(rlm),(opq),(dmn),(stl),(mqop), \
(malgo),(uh),(utf),(resp),MHD_NO)
/**
* Create action to reply with Digest Authentication "challenge".
*
* The @a response must have #MHD_HTTP_STATUS_UNAUTHORIZED status code.
*
* If the @a response object cannot be extended with the "challenge",
* the @a response is aborted.
*
* @param request the request to create the action for
* @param realm the realm presented to the client
* @param opaque the string for opaque value, can be NULL, but NULL is
* not recommended for better compatibility with clients;
* the recommended format is hex or Base64 encoded string
* @param domain the optional space-separated list of URIs for which the
* same authorisation could be used, URIs can be in form
* "path-absolute" (the path for the same host with initial slash)
* or in form "absolute-URI" (the full path with protocol), in
* any case client may assume that URI is in the same "protection
* space" if it starts with any of values specified here;
* could be NULL (clients typically assume that the same
* credentials could be used for any URI on the same host);
* this list provides information for the client only and does
* not actually restrict anything on the server side
* @param indicate_stale if set to #MHD_YES then indication of stale nonce used
* in the client's request is indicated by adding
* 'stale=true' to the authentication header, this
* instructs the client to retry immediately with the new
* nonce and the same credentials, without asking user
* for the new password
* @param mqop the QOP to use
* @param algo digest algorithm to use; if several algorithms are allowed
* then one challenge for each allowed algorithm is added
* @param userhash_support if set to #MHD_YES then support of userhash is
* indicated, allowing client to provide
* hash("username:realm") instead of the username in
* clear text;
* note that clients are allowed to provide the username
* in cleartext even if this parameter set to non-zero;
* when userhash is used, application must be ready to
* identify users by provided userhash value instead of
* username; see #MHD_digest_auth_calc_userhash() and
* #MHD_digest_auth_calc_userhash_hex()
* @param prefer_utf8 if not set to #MHD_NO, parameter 'charset=UTF-8' is
* added, indicating for the client that UTF-8 encoding for
* the username is preferred
* @param response the response to update; should contain the "access denied"
* body;
* note: this function sets the "WWW Authenticate" header and
* the caller should not set this header;
* the response must have #MHD_HTTP_STATUS_UNAUTHORIZED status
* code;
* the NULL is tolerated (the result is
* #MHD_SC_RESP_POINTER_NULL)
* @return pointer to the action, the action must be consumed
* otherwise response object may leak;
* NULL if failed or if any action has been already created for
* the @a request;
* when failed the response object is consumed and need not
* to be "destroyed"
* @ingroup authentication
*/
#define MHD_action_digest_auth_challenge_a(rq,rlm,opq,dmn,stl,mqop,malgo, \
uh,utf,resp) \
MHD_action_digest_auth_challenge ((rq),(rlm),(opq),(dmn),(stl),(mqop), \
(malgo),(uh),(utf),(resp),MHD_YES)
#endif /* ! MHD_NO_STATIC_INLINE */
/**
+281 -35
View File
@@ -3325,20 +3325,32 @@ MHD_FN_PAR_CSTR_ (4);
/**
* Create an action to request authentication from the client
* Add Digest Authentication "challenge" to the response.
*
* If @a mqop allows both RFC 2069 (MHD_DIGEST_AUTH_QOP_NONE) and QOP with
* value, then response is formed like if MHD_DIGEST_AUTH_QOP_NONE bit was
* not set, because such response should be backward-compatible with RFC 2069.
* The response must have #MHD_HTTP_STATUS_UNAUTHORIZED status code.
*
* If @a mqop allows both RFC 2069 (#MHD_DIGEST_AUTH_QOP_NONE) and other QOP
* values, then the "challenge" is formed like if MHD_DIGEST_AUTH_QOP_NONE bit
* was not set, because such "challenge" should be backward-compatible with
* RFC 2069.
*
* If @a mqop allows only MHD_DIGEST_AUTH_MULT_QOP_NONE, then the response is
* formed in strict accordance with RFC 2069 (no 'qop', no 'userhash', no
* 'charset'). For better compatibility with clients, it is recommended (but
* not required) to set @a domain to NULL in this mode.
*
* At most one action can be created for any request.
* New nonces are generated each time when the resulting response is used.
*
* @param request the request
* See RFC 7616, section 3.3 for details.
*
* @param response the response to update; should contain the "access denied"
* body;
* note: this function sets the "WWW Authenticate" header and
* the caller should not set this header;
* the response must have #MHD_HTTP_STATUS_UNAUTHORIZED status
* code;
* the NULL is tolerated (the result is
* #MHD_SC_RESP_POINTER_NULL)
* @param realm the realm presented to the client
* @param opaque the string for opaque value, can be NULL, but NULL is
* not recommended for better compatibility with clients;
@@ -3353,24 +3365,17 @@ MHD_FN_PAR_CSTR_ (4);
* credentials could be used for any URI on the same host);
* this list provides information for the client only and does
* not actually restrict anything on the server side
* @param response the reply to send; should contain the "access denied"
* body;
* note: this function sets the "WWW Authenticate" header and
* the caller should not set this header;
* the response must have #MHD_HTTP_STATUS_FORBIDDEN status
* code, must not have #MHD_R_O_REUSABLE enabled;
* the NULL is tolerated (the result is NULL)
* @param signal_stale if set to #MHD_YES then indication of stale nonce used in
* the client's request is signalled by adding 'stale=true'
* to the authentication header, this instructs the client
* to retry immediately with the new nonce and the same
* credentials, without asking user for the new password
* @param indicate_stale if set to #MHD_YES then indication of stale nonce used
* in the client's request is indicated by adding
* 'stale=true' to the authentication header, this
* instructs the client to retry immediately with the new
* nonce and the same credentials, without asking user
* for the new password
* @param mqop the QOP to use
* @param algo digest algorithm to use; if several algorithms are allowed
* then MD5 is preferred (currently, may be changed in next
* versions)
* @param userhash_support if set to non-zero value (#MHD_YES) then support of
* userhash is indicated, allowing client to provide
* then one challenge for each allowed algorithm is added
* @param userhash_support if set to #MHD_YES then support of userhash is
* indicated, allowing client to provide
* hash("username:realm") instead of the username in
* clear text;
* note that clients are allowed to provide the username
@@ -3382,24 +3387,265 @@ MHD_FN_PAR_CSTR_ (4);
* @param prefer_utf8 if not set to #MHD_NO, parameter 'charset=UTF-8' is
* added, indicating for the client that UTF-8 encoding for
* the username is preferred
* @return pointer to the action on success,
* NULL on failure
* @return #MHD_SC_OK if succeed,
* #MHD_SC_TOO_LATE if the response has been already "frozen" (used to
* create an action),
* #MHD_SC_RESP_HEADERS_CONFLICT if Digest Authentication "challenge"
* has been added already,
* #MHD_SC_RESP_POINTER_NULL if @a response is NULL,
* #MHD_SC_RESP_HTTP_CODE_NOT_SUITABLE is response status code is wrong,
* #MHD_SC_RESP_HEADER_VALUE_INVALID if @a realm, @a opaque or @a domain
* have wrong characters or zero length (for @a realm),
* #MHD_SC_RESPONSE_HEADER_MEM_ALLOC_FAILED if memory allocation failed,
* or other error code if failed
* @ingroup authentication
*/
MHD_EXTERN_ const struct MHD_Action *
MHD_action_digest_auth_required_response (
struct MHD_Request *request,
const char *realm,
const char *opaque,
const char *domain,
struct MHD_Response *response,
enum MHD_Bool signal_stale,
MHD_EXTERN_ enum MHD_StatusCode
MHD_response_add_auth_digest_challenge (
struct MHD_Response *MHD_RESTRICT response,
const char *MHD_RESTRICT realm,
const char *MHD_RESTRICT opaque,
const char *MHD_RESTRICT domain,
enum MHD_Bool indicate_stale,
enum MHD_DigestAuthMultiQOP mqop,
enum MHD_DigestAuthMultiAlgo algo,
enum MHD_DigestAuthMultiAlgo malgo,
enum MHD_Bool userhash_support,
enum MHD_Bool prefer_utf8)
MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_CSTR_ (2)
MHD_FN_PAR_CSTR_ (3) MHD_FN_PAR_CSTR_ (4) MHD_FN_PAR_NONNULL_ (5);
MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_CSTR_ (2)
MHD_FN_PAR_CSTR_ (3) MHD_FN_PAR_CSTR_ (4);
#ifndef MHD_NO_STATIC_INLINE
/**
* Create action to reply with Digest Authentication "challenge".
*
* The @a response must have #MHD_HTTP_STATUS_UNAUTHORIZED status code.
*
* See RFC 7616, section 3.3 for details.
*
* @param request the request to create the action for
* @param realm the realm presented to the client
* @param opaque the string for opaque value, can be NULL, but NULL is
* not recommended for better compatibility with clients;
* the recommended format is hex or Base64 encoded string
* @param domain the optional space-separated list of URIs for which the
* same authorisation could be used, URIs can be in form
* "path-absolute" (the path for the same host with initial slash)
* or in form "absolute-URI" (the full path with protocol), in
* any case client may assume that URI is in the same "protection
* space" if it starts with any of values specified here;
* could be NULL (clients typically assume that the same
* credentials could be used for any URI on the same host);
* this list provides information for the client only and does
* not actually restrict anything on the server side
* @param indicate_stale if set to #MHD_YES then indication of stale nonce used
* in the client's request is indicated by adding
* 'stale=true' to the authentication header, this
* instructs the client to retry immediately with the new
* nonce and the same credentials, without asking user
* for the new password
* @param mqop the QOP to use
* @param algo digest algorithm to use; if several algorithms are allowed
* then one challenge for each allowed algorithm is added
* @param userhash_support if set to #MHD_YES then support of userhash is
* indicated, allowing client to provide
* hash("username:realm") instead of the username in
* clear text;
* note that clients are allowed to provide the username
* in cleartext even if this parameter set to non-zero;
* when userhash is used, application must be ready to
* identify users by provided userhash value instead of
* username; see #MHD_digest_auth_calc_userhash() and
* #MHD_digest_auth_calc_userhash_hex()
* @param prefer_utf8 if not set to #MHD_NO, parameter 'charset=UTF-8' is
* added, indicating for the client that UTF-8 encoding for
* the username is preferred
* @param response the response to update; should contain the "access denied"
* body;
* note: this function sets the "WWW Authenticate" header and
* the caller should not set this header;
* the response must have #MHD_HTTP_STATUS_UNAUTHORIZED status
* code;
* the NULL is tolerated (the result is
* #MHD_SC_RESP_POINTER_NULL)
* @param abort_if_failed if set to #MHD_NO the response will be used even if
* failed to add Basic Authentication "challenge",
* if not set to #MHD_NO the request will be aborted
* if the "challenge" could not be added.
* @return pointer to the action, the action must be consumed
* otherwise response object may leak;
* NULL if failed or if any action has been already created for
* the @a request;
* when failed the response object is consumed and need not
* to be "destroyed"
* @ingroup authentication
*/
MHD_STATIC_INLINE_
MHD_FN_PAR_NONNULL_ (1)
MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_CSTR_ (2)
const struct MHD_Action *
MHD_action_digest_auth_challenge (struct MHD_Request *MHD_RESTRICT request,
const char *MHD_RESTRICT realm,
const char *MHD_RESTRICT opaque,
const char *MHD_RESTRICT domain,
enum MHD_Bool indicate_stale,
enum MHD_DigestAuthMultiQOP mqop,
enum MHD_DigestAuthMultiAlgo malgo,
enum MHD_Bool userhash_support,
enum MHD_Bool prefer_utf8,
struct MHD_Response *MHD_RESTRICT response,
enum MHD_Bool abort_if_failed)
{
if ((MHD_SC_OK !=
MHD_response_add_auth_digest_challenge (response, realm, opaque, domain,
indicate_stale, mqop, malgo,
userhash_support, prefer_utf8))
&& (MHD_NO != abort_if_failed))
{
MHD_response_destroy (response);
return MHD_action_abort_request (request);
}
return MHD_action_from_response (request, response);
}
MHD_STATIC_INLINE_END_
/**
* Create action to reply with Digest Authentication "challenge".
*
* The @a response must have #MHD_HTTP_STATUS_UNAUTHORIZED status code.
*
* If the @a response object cannot be extended with the "challenge",
* the @a response is used to reply without the "challenge".
*
* @param request the request to create the action for
* @param realm the realm presented to the client
* @param opaque the string for opaque value, can be NULL, but NULL is
* not recommended for better compatibility with clients;
* the recommended format is hex or Base64 encoded string
* @param domain the optional space-separated list of URIs for which the
* same authorisation could be used, URIs can be in form
* "path-absolute" (the path for the same host with initial slash)
* or in form "absolute-URI" (the full path with protocol), in
* any case client may assume that URI is in the same "protection
* space" if it starts with any of values specified here;
* could be NULL (clients typically assume that the same
* credentials could be used for any URI on the same host);
* this list provides information for the client only and does
* not actually restrict anything on the server side
* @param indicate_stale if set to #MHD_YES then indication of stale nonce used
* in the client's request is indicated by adding
* 'stale=true' to the authentication header, this
* instructs the client to retry immediately with the new
* nonce and the same credentials, without asking user
* for the new password
* @param mqop the QOP to use
* @param algo digest algorithm to use; if several algorithms are allowed
* then one challenge for each allowed algorithm is added
* @param userhash_support if set to #MHD_YES then support of userhash is
* indicated, allowing client to provide
* hash("username:realm") instead of the username in
* clear text;
* note that clients are allowed to provide the username
* in cleartext even if this parameter set to non-zero;
* when userhash is used, application must be ready to
* identify users by provided userhash value instead of
* username; see #MHD_digest_auth_calc_userhash() and
* #MHD_digest_auth_calc_userhash_hex()
* @param prefer_utf8 if not set to #MHD_NO, parameter 'charset=UTF-8' is
* added, indicating for the client that UTF-8 encoding for
* the username is preferred
* @param response the response to update; should contain the "access denied"
* body;
* note: this function sets the "WWW Authenticate" header and
* the caller should not set this header;
* the response must have #MHD_HTTP_STATUS_UNAUTHORIZED status
* code;
* the NULL is tolerated (the result is
* #MHD_SC_RESP_POINTER_NULL)
* @return pointer to the action, the action must be consumed
* otherwise response object may leak;
* NULL if failed or if any action has been already created for
* the @a request;
* when failed the response object is consumed and need not
* to be "destroyed"
* @ingroup authentication
*/
#define MHD_action_digest_auth_challenge_p(rq,rlm,opq,dmn,stl,mqop,malgo, \
uh,utf,resp) \
MHD_action_digest_auth_challenge ((rq),(rlm),(opq),(dmn),(stl),(mqop), \
(malgo),(uh),(utf),(resp),MHD_NO)
/**
* Create action to reply with Digest Authentication "challenge".
*
* The @a response must have #MHD_HTTP_STATUS_UNAUTHORIZED status code.
*
* If the @a response object cannot be extended with the "challenge",
* the @a response is aborted.
*
* @param request the request to create the action for
* @param realm the realm presented to the client
* @param opaque the string for opaque value, can be NULL, but NULL is
* not recommended for better compatibility with clients;
* the recommended format is hex or Base64 encoded string
* @param domain the optional space-separated list of URIs for which the
* same authorisation could be used, URIs can be in form
* "path-absolute" (the path for the same host with initial slash)
* or in form "absolute-URI" (the full path with protocol), in
* any case client may assume that URI is in the same "protection
* space" if it starts with any of values specified here;
* could be NULL (clients typically assume that the same
* credentials could be used for any URI on the same host);
* this list provides information for the client only and does
* not actually restrict anything on the server side
* @param indicate_stale if set to #MHD_YES then indication of stale nonce used
* in the client's request is indicated by adding
* 'stale=true' to the authentication header, this
* instructs the client to retry immediately with the new
* nonce and the same credentials, without asking user
* for the new password
* @param mqop the QOP to use
* @param algo digest algorithm to use; if several algorithms are allowed
* then one challenge for each allowed algorithm is added
* @param userhash_support if set to #MHD_YES then support of userhash is
* indicated, allowing client to provide
* hash("username:realm") instead of the username in
* clear text;
* note that clients are allowed to provide the username
* in cleartext even if this parameter set to non-zero;
* when userhash is used, application must be ready to
* identify users by provided userhash value instead of
* username; see #MHD_digest_auth_calc_userhash() and
* #MHD_digest_auth_calc_userhash_hex()
* @param prefer_utf8 if not set to #MHD_NO, parameter 'charset=UTF-8' is
* added, indicating for the client that UTF-8 encoding for
* the username is preferred
* @param response the response to update; should contain the "access denied"
* body;
* note: this function sets the "WWW Authenticate" header and
* the caller should not set this header;
* the response must have #MHD_HTTP_STATUS_UNAUTHORIZED status
* code;
* the NULL is tolerated (the result is
* #MHD_SC_RESP_POINTER_NULL)
* @return pointer to the action, the action must be consumed
* otherwise response object may leak;
* NULL if failed or if any action has been already created for
* the @a request;
* when failed the response object is consumed and need not
* to be "destroyed"
* @ingroup authentication
*/
#define MHD_action_digest_auth_challenge_a(rq,rlm,opq,dmn,stl,mqop,malgo, \
uh,utf,resp) \
MHD_action_digest_auth_challenge ((rq),(rlm),(opq),(dmn),(stl),(mqop), \
(malgo),(uh),(utf),(resp),MHD_YES)
#endif /* ! MHD_NO_STATIC_INLINE */
/**
+10
View File
@@ -1621,6 +1621,16 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
* cannot be provided due to some error or other reasons.
*/
MHD_SC_INFO_GET_TYPE_UNAVAILALBE = 60204
,
/**
* The type of the Digest Auth algorithm is unknown or not supported.
*/
MHD_SC_AUTH_DIGEST_ALGO_NOT_SUPPORTED = 60240
,
/**
* The the Digest Auth QOP value is unknown or not supported.
*/
MHD_SC_AUTH_DIGEST_QOP_NOT_SUPPORTED = 60241
};
/**
+8
View File
@@ -110,6 +110,10 @@ auth_basic_OPTSOURCES = \
auth_basic.c auth_basic.h \
response_auth_basic.c
auth_digest_OPTSOURCES = \
mhd_digest_auth_data.h mhd_auth_digest_hdr.h \
response_auth_digest.c response_auth_digest.h
upgrade_OPTSOURCES = \
mhd_upgrade.h \
upgrade_prep.c upgrade_prep.h \
@@ -146,6 +150,10 @@ if MHD_SUPPORT_AUTH_BASIC
libmicrohttpd2_la_SOURCES += $(auth_basic_OPTSOURCES)
endif
if MHD_SUPPORT_AUTH_DIGEST
libmicrohttpd2_la_SOURCES += $(auth_digest_OPTSOURCES)
endif
if MHD_UPGRADE_SUPPORT
libmicrohttpd2_la_SOURCES += $(upgrade_OPTSOURCES)
endif
+66
View File
@@ -0,0 +1,66 @@
/*
This file is part of GNU libmicrohttpd
Copyright (C) 2024 Evgeny Grin (Karlson2k)
GNU libmicrohttpd is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
GNU libmicrohttpd 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file src/mhd2/mhd_auth_digest_hdr.h
* @brief The definition of the Digest Auth response header structure
* @author Karlson2k (Evgeny Grin)
*/
#ifndef MHD_AUTH_DIGEST_HDR_H
#define MHD_AUTH_DIGEST_HDR_H 1
#include "mhd_sys_options.h"
#ifndef MHD_SUPPORT_AUTH_DIGEST
#error This header should be used only if Digest Auth is build
#endif
#include "mhd_str_types.h"
#include "mhd_dlinked_list.h"
struct MHD_Response; /* forward declaration */
struct mhd_RespAuthDigestHeader; /* forward declaration */
mhd_DLINKEDL_STRUCTS_DEFS (mhd_RespAuthDigestHeader);
/**
* The Digest Auth response header (the challenge)
*/
struct mhd_RespAuthDigestHeader
{
/**
* The complete header string, including the name, the colon, the value and
* CRLF terminating chars
*/
struct MHD_String hdr;
/**
* The position of the nonce placeholder in the @a hdr
*/
size_t nonce_pos;
/**
* The links to the other headers for the same response
*/
mhd_DLNKDL_LINKS_TYPE (mhd_RespAuthDigestHeader) auth_d_hdrs;
};
#endif /* ! MHD_AUTH_DIGEST_HDR_H */
+53
View File
@@ -0,0 +1,53 @@
/*
This file is part of GNU libmicrohttpd
Copyright (C) 2024 Evgeny Grin (Karlson2k)
GNU libmicrohttpd is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
GNU libmicrohttpd 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file src/mhd2/mhd_digest_auth_data.h
* @brief The macros and other data for Digest Auth
* @author Karlson2k (Evgeny Grin)
*/
#ifndef MHD_DIGEST_AUTH_DATA_H
#define MHD_DIGEST_AUTH_DATA_H 1
#include "mhd_sys_options.h"
/**
* The name of the Auth scheme
*/
#define mhd_AUTH_DIGEST_SCHEME "Digest"
/**
* The length of the random part of the nonce (in chars)
*/
#define mhd_AUTH_DIGEST_NONCE_RAND_LEN 64
/**
* The length of the validity time part of the nonce (in chars)
*/
#define mhd_AUTH_DIGEST_NONCE_VALD_LEN 8
/**
* The total length of the nonce (in chars)
*/
#define mhd_AUTH_DIGEST_NONCE_LEN \
(mhd_AUTH_DIGEST_NONCE_RAND_LEN + mhd_AUTH_DIGEST_NONCE_VALD_LEN)
#endif /* ! MHD_DIGEST_AUTH_DATA_H */
+10
View File
@@ -36,6 +36,9 @@
#include "mhd_dlinked_list.h"
#include "mhd_str_types.h"
#ifdef MHD_SUPPORT_AUTH_DIGEST
# include "mhd_auth_digest_hdr.h"
#endif
#include "mhd_iovec.h"
@@ -356,6 +359,13 @@ struct MHD_Response
*/
mhd_DLNKDL_LIST (mhd_ResponseHeader,headers);
#ifdef MHD_SUPPORT_AUTH_DIGEST
/**
* The double linked list of the Digest Auth response headers
*/
mhd_DLNKDL_LIST (mhd_RespAuthDigestHeader,auth_d_hdrs);
#endif
/**
* Special data for internal error responses
*/
+521
View File
@@ -0,0 +1,521 @@
/*
This file is part of GNU libmicrohttpd
Copyright (C) 2022-2024 Evgeny Grin (Karlson2k)
GNU libmicrohttpd is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
GNU libmicrohttpd 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file src/mhd2/response_auth_digest.c
* @brief The definitions of MHD_response_add_auth_basic_challenge() function
* @author Karlson2k (Evgeny Grin)
*/
#include "mhd_sys_options.h"
#include "sys_bool_type.h"
#include "mhd_unreachable.h"
#include "mhd_assert.h"
#include "mhd_digest_auth_data.h"
#include "mhd_response.h"
#include "mhd_locks.h"
#include "mhd_str_types.h"
#include <string.h>
#include "sys_malloc.h"
#include "mhd_str_macros.h"
#include "mhd_str.h"
#include "mhd_public_api.h"
#include "response_auth_digest.h"
#include "mhd_auth_digest_hdr.h"
MHD_INTERNAL
MHD_FN_PAR_NONNULL_ (1) void
mhd_response_remove_auth_digest_headers (struct MHD_Response*response)
{
struct mhd_RespAuthDigestHeader *hdr_d;
for (hdr_d = mhd_DLINKEDL_GET_LAST (response, auth_d_hdrs);
NULL != hdr_d;
hdr_d = mhd_DLINKEDL_GET_LAST (response, auth_d_hdrs))
{
mhd_DLINKEDL_DEL (response, hdr_d, auth_d_hdrs);
free (hdr_d);
}
}
/**
* Create and add Digest Auth challenge header with specified algorithm
* @param response the response to update
* @param rlm the realm to use
* @param opq the "opaque" string to use
* @param dmn the "domain" string to use
* @param indicate_stale whether to indicate "stale" nonce
* @param qop_none whether to use RFC 2069 subset only
* @param algo the algorithm to use
* @param userhash_support whether to indicate support for "userhash"
* @param prefer_utf8 whether to indicate UTF-8 support
* @return #MHD_SC_OK on success,
* error code otherwise
*/
static MHD_FN_PAR_NONNULL_ALL_ enum MHD_StatusCode
response_add_auth_digest_challenge_alg (
struct MHD_Response *restrict response,
const struct MHD_String *restrict rlm,
const struct MHD_StringNullable *restrict opq,
const struct MHD_StringNullable *restrict dmn,
enum MHD_Bool indicate_stale,
bool qop_none,
enum MHD_DigestAuthAlgo algo,
enum MHD_Bool userhash_support,
enum MHD_Bool prefer_utf8)
{
static const struct MHD_String empty_str =
mhd_MSTR_INIT ("");
static const struct MHD_String hdr_pref_realm_pref =
mhd_MSTR_INIT (MHD_HTTP_HEADER_WWW_AUTHENTICATE ": " \
mhd_AUTH_DIGEST_SCHEME " realm=\"");
static const struct MHD_String qop_str =
mhd_MSTR_INIT (", qop=\"auth\"");
#ifdef MHD_SUPPORT_MD5
static const struct MHD_String algo_mh5_str =
mhd_MSTR_INIT (", algorithm=MD5");
#endif
#ifdef MHD_SUPPORT_SHA256
static const struct MHD_String algo_sha256_str =
mhd_MSTR_INIT (", algorithm=SHA-256");
#endif
#ifdef MHD_SUPPORT_SHA512_256
static const struct MHD_String algo_sha512_256_str =
mhd_MSTR_INIT (", algorithm=SHA-512-256");
#endif
static const struct MHD_String nonce_str =
mhd_MSTR_INIT (", nonce=\"" \
"00000000000000000000000000000000" \
"00000000000000000000000000000000" \
"00000000" \
"\"");
static size_t nonce_off = 9; /* Position of nonce value in the nonce_str */
static const struct MHD_String opaque_pref =
mhd_MSTR_INIT (", opaque=\"");
static const struct MHD_String domain_pref =
mhd_MSTR_INIT (", domain=\"");
static const struct MHD_String stale_str =
mhd_MSTR_INIT (", stale=true");
static const struct MHD_String charset_str =
mhd_MSTR_INIT (", charset=UTF-8");
static const struct MHD_String userhash_str =
mhd_MSTR_INIT (", userhash=true");
/* Header content:
+ Scheme name and space;
+ realm, quoted;
+ qop="auth", quoted (optional);
+ algorithm, NOT quoted (optional);
+ nonce (placeholder), quoted;
+ opaque, quoted (optional);
+ domain, quoted (optional);
+ stale=true (optional);
+ charset=UTF-8 (optional);
+ userhash=true (optional).
*/
struct MHD_String algo_str;
char *hdr_str;
size_t hdr_maxlen;
struct mhd_RespAuthDigestHeader *new_hdr;
size_t pos;
size_t elm_len;
mhd_assert ('0' == nonce_str.cstr[nonce_off]);
mhd_assert ('0' == nonce_str.cstr[nonce_off + mhd_AUTH_DIGEST_NONCE_LEN]);
mhd_assert ('"' == nonce_str.cstr[nonce_off - 1]);
mhd_assert ('"' == nonce_str.cstr[nonce_off + mhd_AUTH_DIGEST_NONCE_LEN + 1]);
#ifdef MHD_SUPPORT_MD5
if (MHD_DIGEST_AUTH_ALGO_MD5 == algo)
algo_str = qop_none ? empty_str : algo_mh5_str;
#endif
#ifdef MHD_SUPPORT_SHA256
else if (MHD_DIGEST_AUTH_ALGO_SHA256 == algo)
algo_str = algo_sha256_str;
#endif
#ifdef MHD_SUPPORT_SHA512_256
else if (MHD_DIGEST_AUTH_ALGO_SHA512_256 == algo)
algo_str = algo_sha512_256_str;
#endif
else
mhd_UNREACHABLE ();
/* ** Calculate the maximum length of the header string ** */
hdr_maxlen = 0;
/* realm */
hdr_maxlen += hdr_pref_realm_pref.len;
hdr_maxlen += rlm->len * 2; /* Double length for quoting */
hdr_maxlen += 1; /* closing quote (") */
/* qop */
hdr_maxlen += qop_str.len;
/* algorithm */
hdr_maxlen += algo_str.len;
/* nonce */
hdr_maxlen += nonce_str.len;
/* opaque */
hdr_maxlen += opaque_pref.len;
hdr_maxlen += opq->len * 2; /* Double length for quoting */
hdr_maxlen += 1; /* closing quote (") */
/* domain */
hdr_maxlen += domain_pref.len;
hdr_maxlen += dmn->len;
hdr_maxlen += 1; /* closing quote (") */
/* stale */
hdr_maxlen += stale_str.len;
/* charset */
hdr_maxlen += charset_str.len;
/* userhash */
hdr_maxlen += userhash_str.len;
/* CRLF */
hdr_maxlen += 2;
/* ** Allocate ** */
new_hdr = (struct mhd_RespAuthDigestHeader *)
malloc (sizeof(struct mhd_RespAuthDigestHeader *)
+ hdr_maxlen + 1);
if (NULL == new_hdr)
return MHD_SC_RESPONSE_HEADER_MEM_ALLOC_FAILED;
hdr_str = (char *) (new_hdr + 1);
/* ** Build the header ** */
pos = 0;
/* realm */
memcpy (hdr_str + pos,
hdr_pref_realm_pref.cstr,
hdr_pref_realm_pref.len);
pos += hdr_pref_realm_pref.len;
elm_len = mhd_str_quote (rlm->cstr,
rlm->len,
hdr_str + pos,
hdr_maxlen - pos);
mhd_assert (0 != elm_len);
pos += elm_len;
hdr_str[pos++] = '"';
/* qop */
if (! qop_none)
{
memcpy (hdr_str + pos,
qop_str.cstr,
qop_str.len);
pos += qop_str.len;
}
/* algorithm */
if (0 != algo_str.len)
{
memcpy (hdr_str + pos,
algo_str.cstr,
algo_str.len);
pos += algo_str.len;
}
/* nonce */
memcpy (hdr_str + pos,
nonce_str.cstr,
nonce_str.len);
new_hdr->nonce_pos = pos + nonce_off;
pos += nonce_str.len;
/* opaque */
if (0 != opq->len)
{
memcpy (hdr_str + pos,
opaque_pref.cstr,
opaque_pref.len);
pos += opaque_pref.len;
elm_len = mhd_str_quote (opq->cstr,
opq->len,
hdr_str + pos,
hdr_maxlen - pos);
mhd_assert (0 != elm_len);
pos += elm_len;
hdr_str[pos++] = '"';
}
/* domain */
if (0 != dmn->len)
{
memcpy (hdr_str + pos,
domain_pref.cstr,
domain_pref.len);
pos += domain_pref.len;
memcpy (hdr_str + pos,
dmn->cstr,
dmn->len);
pos += dmn->len;
hdr_str[pos++] = '"';
}
/* stale */
if (MHD_NO != indicate_stale)
{
memcpy (hdr_str + pos,
stale_str.cstr,
stale_str.len);
pos += stale_str.len;
}
/* charset */
if ((! qop_none) &&
(MHD_NO != prefer_utf8))
{
memcpy (hdr_str + pos,
charset_str.cstr,
charset_str.len);
pos += charset_str.len;
}
/* userhash */
if ((! qop_none) &&
(MHD_NO != userhash_support))
{
memcpy (hdr_str + pos,
userhash_str.cstr,
userhash_str.len);
pos += userhash_str.len;
}
/* CRLF */
hdr_str[pos++] = '\r';
hdr_str[pos++] = '\n';
mhd_assert (pos <= hdr_maxlen);
hdr_str[pos] = 0; /* Zero-terminate the string */
if (1)
{ /* Try to shrink malloc'ed area */
void *new_ptr;
new_ptr = realloc (new_hdr,
sizeof(struct mhd_RespAuthDigestHeader)
+ pos);
/* Just use the old pointer if realloc() failed */
if (NULL != new_ptr)
new_hdr = (struct mhd_RespAuthDigestHeader *) new_ptr;
}
new_hdr->hdr.cstr = (char *) (new_hdr + 1);
new_hdr->hdr.len = pos;
mhd_assert (0 == \
memcmp (new_hdr->hdr.cstr, \
MHD_HTTP_HEADER_WWW_AUTHENTICATE ": ", \
mhd_SSTR_LEN (MHD_HTTP_HEADER_WWW_AUTHENTICATE ": ")));
mhd_assert (0 == new_hdr->hdr.cstr[new_hdr->hdr.len]);
mhd_DLINKEDL_INIT_LINKS (new_hdr, auth_d_hdrs);
mhd_DLINKEDL_INS_LAST (response, new_hdr, auth_d_hdrs);
return MHD_SC_OK;
}
/**
* Create and add Digest Auth challenge headers for all specified algorithms
* @param response the response to update
* @param realm the real to use
* @param opaque the "opaque" string, could be NULL
* @param domain the "domain" string, could be NULL
* @param indicate_stale whether to indicate "stale" nonce
* @param mqop the QOP values to use
* @param malgo the algorithms to use
* @param userhash_support whether to indicate support for "userhash"
* @param prefer_utf8 whether to indicate UTF-8 support
* @return #MHD_SC_OK on success,
* error code otherwise
*/
static MHD_FN_PAR_NONNULL_ (1)
MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_CSTR_ (2)
MHD_FN_PAR_CSTR_ (3) MHD_FN_PAR_CSTR_ (4) enum MHD_StatusCode
response_add_auth_digest_challenge_int (struct MHD_Response *restrict response,
const char *restrict realm,
const char *restrict opaque,
const char *restrict domain,
enum MHD_Bool indicate_stale,
enum MHD_DigestAuthMultiQOP mqop,
enum MHD_DigestAuthMultiAlgo malgo,
enum MHD_Bool userhash_support,
enum MHD_Bool prefer_utf8)
{
const struct MHD_String rlm = { strlen (realm), realm };
const struct MHD_StringNullable opq =
{ (NULL != opaque ? strlen (opaque) : 0), opaque };
const struct MHD_StringNullable dmn =
{ (NULL != domain ? strlen (domain) : 0), domain };
const bool qop_none =
(0 != (MHD_DIGEST_AUTH_QOP_NONE & ((unsigned int) mqop)));
enum MHD_StatusCode res;
/* Check validity of the input data */
if (0 == rlm.len)
return MHD_SC_RESP_HEADER_VALUE_INVALID; /* Failure exit point */
if ((NULL != memchr (rlm.cstr, '\n', rlm.len)) ||
(NULL != memchr (rlm.cstr, '\r', rlm.len)))
return MHD_SC_RESP_HEADER_VALUE_INVALID; /* Failure exit point */
if ((0 != opq.len) &&
((NULL != memchr (opq.cstr, '\n', opq.len)) ||
(NULL != memchr (opq.cstr, '\r', opq.len))))
return MHD_SC_RESP_HEADER_VALUE_INVALID; /* Failure exit point */
if ((0 != dmn.len) &&
((NULL != memchr (dmn.cstr, '\n', dmn.len)) ||
(NULL != memchr (dmn.cstr, '\r', dmn.len)) ||
(NULL != memchr (dmn.cstr, '"', dmn.len))))
return MHD_SC_RESP_HEADER_VALUE_INVALID; /* Failure exit point */
if (0 == (MHD_DIGEST_AUTH_ALGO_NON_SESSION & ((unsigned int) malgo)))
return MHD_SC_AUTH_DIGEST_ALGO_NOT_SUPPORTED; /* Failure exit point */
if (0 == ((MHD_DIGEST_AUTH_QOP_NONE | MHD_DIGEST_AUTH_QOP_AUTH)
& ((unsigned int) mqop)))
return MHD_SC_AUTH_DIGEST_QOP_NOT_SUPPORTED; /* Failure exit point */
res = MHD_SC_OK;
#ifdef MHD_SUPPORT_MD5
if ((MHD_SC_OK == res) &&
(0 == (MHD_DIGEST_BASE_ALGO_MD5 & ((unsigned int) malgo))))
res = response_add_auth_digest_challenge_alg (response,
&rlm,
&opq,
&dmn,
indicate_stale,
qop_none,
MHD_DIGEST_AUTH_ALGO_MD5,
userhash_support,
prefer_utf8);
#endif
#ifdef MHD_SUPPORT_SHA256
if ((MHD_SC_OK == res) &&
(0 == (MHD_DIGEST_BASE_ALGO_SHA256 & ((unsigned int) malgo))))
res = response_add_auth_digest_challenge_alg (response,
&rlm,
&opq,
&dmn,
indicate_stale,
qop_none,
MHD_DIGEST_AUTH_ALGO_SHA256,
userhash_support,
prefer_utf8);
#endif
#ifdef MHD_SUPPORT_SHA512_256
if ((MHD_SC_OK == res) &&
(0 == (MHD_DIGEST_BASE_ALGO_SHA256 & ((unsigned int) malgo))))
res = response_add_auth_digest_challenge_alg (
response,
&rlm,
&opq,
&dmn,
indicate_stale,
qop_none,
MHD_DIGEST_AUTH_ALGO_SHA512_256,
userhash_support,
prefer_utf8);
#endif
if (MHD_SC_OK != res)
{
mhd_response_remove_auth_digest_headers (response);
return res; /* Failure exit point */
}
if (NULL == mhd_DLINKEDL_GET_FIRST (response, auth_d_hdrs))
return MHD_SC_AUTH_DIGEST_ALGO_NOT_SUPPORTED; /* Failure exit point */
return MHD_SC_OK; /* Success exit point */
}
MHD_EXTERN_
MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_CSTR_ (2)
MHD_FN_PAR_CSTR_ (3) MHD_FN_PAR_CSTR_ (4) enum MHD_StatusCode
MHD_response_add_auth_digest_challenge (
struct MHD_Response *MHD_RESTRICT response,
const char *MHD_RESTRICT realm,
const char *MHD_RESTRICT opaque,
const char *MHD_RESTRICT domain,
enum MHD_Bool indicate_stale,
enum MHD_DigestAuthMultiQOP mqop,
enum MHD_DigestAuthMultiAlgo malgo,
enum MHD_Bool userhash_support,
enum MHD_Bool prefer_utf8)
{
bool need_unlock;
enum MHD_StatusCode res;
if (NULL == response)
return MHD_SC_RESP_POINTER_NULL;
if (response->frozen)
return MHD_SC_TOO_LATE;
if (MHD_HTTP_STATUS_UNAUTHORIZED != response->sc)
return MHD_SC_RESP_HTTP_CODE_NOT_SUITABLE;
if (response->reuse.reusable)
{
need_unlock = true;
if (! mhd_mutex_lock (&(response->reuse.settings_lock)))
return MHD_SC_RESPONSE_MUTEX_LOCK_FAILED;
mhd_assert (1 == mhd_atomic_counter_get (&(response->reuse.counter)));
}
else
need_unlock = false;
if (response->frozen) /* Re-check with the lock held */
res = MHD_SC_TOO_LATE;
else if (NULL != mhd_DLINKEDL_GET_FIRST (response, auth_d_hdrs))
res = MHD_SC_RESP_HEADERS_CONFLICT;
else
res = response_add_auth_digest_challenge_int (response,
realm,
opaque,
domain,
indicate_stale,
mqop,
malgo,
userhash_support,
prefer_utf8);
if (need_unlock)
mhd_mutex_unlock_chk (&(response->reuse.settings_lock));
return res;
}
+42
View File
@@ -0,0 +1,42 @@
/*
This file is part of GNU libmicrohttpd
Copyright (C) 2024 Evgeny Grin (Karlson2k)
GNU libmicrohttpd is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
GNU libmicrohttpd 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file src/mhd2/response_auth_digest.h
* @brief The declaration of the Digest Auth response header helper functions
* @author Karlson2k (Evgeny Grin)
*/
#ifndef MHD_RESPONSE_AUTH_DIGEST_H
#define MHD_RESPONSE_AUTH_DIGEST_H 1
#include "mhd_sys_options.h"
struct MHD_Response; /* forward declaration */
/**
* Remove all Digest Auth headers (if any) from the response
* @param response the response to clean
*/
MHD_INTERNAL void
mhd_response_remove_auth_digest_headers (struct MHD_Response*response)
MHD_FN_PAR_NONNULL_ (1);
#endif /* ! MHD_RESPONSE_AUTH_DIGEST_H */
+7
View File
@@ -40,6 +40,10 @@
#include "response_funcs.h"
#include "response_from.h"
#ifdef MHD_SUPPORT_AUTH_DIGEST
# include "response_auth_digest.h"
#endif
#define mhd_RESPONSE_DESTOYED "Attempt to use destroyed response, " \
"re-use non-reusable response or wrong MHD_Response pointer"
@@ -52,6 +56,9 @@
static MHD_FN_PAR_NONNULL_ (1) void
response_full_deinit (struct MHD_Response *restrict r)
{
#ifdef MHD_SUPPORT_AUTH_DIGEST
mhd_response_remove_auth_digest_headers (r);
#endif
mhd_response_remove_all_headers (r);
if (NULL != r->special_resp.spec_hdr)
free (r->special_resp.spec_hdr);
+3
View File
@@ -68,6 +68,9 @@ response_create_basic (enum MHD_HTTP_StatusCode sc,
{
#ifndef HAVE_NULL_PTR_ALL_ZEROS
mhd_DLINKEDL_INIT_LIST (r, headers);
#ifdef MHD_SUPPORT_AUTH_DIGEST
mhd_DLINKEDL_INIT_LIST (r, auth_d_hdrs);
#endif
r->free.cb = NULL;
r->free.cls = NULL;
r->special_resp.spec_hdr = NULL;
+10 -10
View File
@@ -175,10 +175,10 @@ if MHD_SUPPORT_AUTH_DIGEST
libmicrohttpd_la_SOURCES += \
digestauth.c digestauth.h \
mhd_bithelpers.h mhd_byteorder.h mhd_align.h
if ENABLE_MD5
if MHD_SUPPORT_MD5
libmicrohttpd_la_SOURCES += \
mhd_md5_wrap.h
if ! ENABLE_MD5_EXT
if ! MHD_MD5_EXTR
libmicrohttpd_la_SOURCES += \
md5.c md5.h
else
@@ -186,10 +186,10 @@ libmicrohttpd_la_SOURCES += \
md5_ext.c md5_ext.h
endif
endif
if ENABLE_SHA256
if MHD_SUPPORT_SHA256
libmicrohttpd_la_SOURCES += \
mhd_sha256_wrap.h
if ! ENABLE_SHA256_EXT
if ! MHD_SHA256_EXTR
libmicrohttpd_la_SOURCES += \
sha256.c sha256.h
else
@@ -197,7 +197,7 @@ libmicrohttpd_la_SOURCES += \
sha256_ext.c sha256_ext.h
endif
endif
if ENABLE_SHA512_256
if MHD_SUPPORT_SHA512_256
libmicrohttpd_la_SOURCES += \
sha512_256.c sha512_256.h
endif
@@ -244,15 +244,15 @@ check_PROGRAMS = \
test_mhd_version \
test_set_panic
if ENABLE_MD5
if MHD_SUPPORT_MD5
check_PROGRAMS += \
test_md5
endif
if ENABLE_SHA256
if MHD_SUPPORT_SHA256
check_PROGRAMS += \
test_sha256
endif
if ENABLE_SHA512_256
if MHD_SUPPORT_SHA512_256
check_PROGRAMS += \
test_sha512_256
endif
@@ -518,7 +518,7 @@ test_http_reasons_SOURCES = \
test_md5_SOURCES = \
test_md5.c test_helpers.h mhd_md5_wrap.h ../include/mhd_options.h
if ! ENABLE_MD5_EXT
if ! MHD_MD5_EXTR
test_md5_SOURCES += \
md5.c md5.h mhd_bithelpers.h mhd_byteorder.h mhd_align.h
test_md5_CPPFLAGS = $(AM_CPPFLAGS)
@@ -536,7 +536,7 @@ endif
test_sha256_SOURCES = \
test_sha256.c test_helpers.h mhd_sha256_wrap.h ../include/mhd_options.h
if ! ENABLE_SHA256_EXT
if ! MHD_SHA256_EXTR
test_sha256_SOURCES += \
sha256.c sha256.h mhd_bithelpers.h mhd_byteorder.h mhd_align.h
test_sha256_CPPFLAGS = $(AM_CPPFLAGS)
+4 -4
View File
@@ -181,18 +181,18 @@ endif
if MHD_SUPPORT_AUTH_DIGEST
if ENABLE_MD5
if MHD_SUPPORT_MD5
THREAD_ONLY_TESTS += \
test_digestauth \
test_digestauth_with_arguments \
test_digestauth_concurrent
endif
if ENABLE_SHA256
if MHD_SUPPORT_SHA256
THREAD_ONLY_TESTS += \
test_digestauth_sha256
endif
if ENABLE_MD5
if MHD_SUPPORT_MD5
check_PROGRAMS += \
test_digestauth_emu_ext \
test_digestauth_emu_ext_oldapi \
@@ -211,7 +211,7 @@ check_PROGRAMS += \
test_digestauth2_oldapi1_bind_all \
test_digestauth2_oldapi1_bind_uri
endif
if ENABLE_SHA256
if MHD_SUPPORT_SHA256
check_PROGRAMS += \
test_digestauth2_sha256 \
test_digestauth2_sha256_userhash \