Coverage Report

Created: 2026-09-03 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mod_auth_openidc/src/proto/dpop.c
Line
Count
Source
1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one
3
 * or more contributor license agreements.  See the NOTICE file
4
 * distributed with this work for additional information
5
 * regarding copyright ownership.  The ASF licenses this file
6
 * to you under the Apache License, Version 2.0 (the
7
 * "License"); you may not use this file except in compliance
8
 * with the License.  You may obtain a copy of the License at
9
 *
10
 *   http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing,
13
 * software distributed under the License is distributed on an
14
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
 * KIND, either express or implied.  See the License for the
16
 * specific language governing permissions and limitations
17
 * under the License.
18
 */
19
20
/***************************************************************************
21
 * Copyright (C) 2017-2026 ZmartZone Holding BV
22
 * All rights reserved.
23
 *
24
 * DISCLAIMER OF WARRANTIES:
25
 *
26
 * THE SOFTWARE PROVIDED HEREUNDER IS PROVIDED ON AN "AS IS" BASIS, WITHOUT
27
 * ANY WARRANTIES OR REPRESENTATIONS EXPRESS, IMPLIED OR STATUTORY; INCLUDING,
28
 * WITHOUT LIMITATION, WARRANTIES OF QUALITY, PERFORMANCE, NONINFRINGEMENT,
29
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  NOR ARE THERE ANY
30
 * WARRANTIES CREATED BY A COURSE OR DEALING, COURSE OF PERFORMANCE OR TRADE
31
 * USAGE.  FURTHERMORE, THERE ARE NO WARRANTIES THAT THE SOFTWARE WILL MEET
32
 * YOUR NEEDS OR BE FREE FROM ERRORS, OR THAT THE OPERATION OF THE SOFTWARE
33
 * WILL BE UNINTERRUPTED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
34
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
35
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES HOWEVER CAUSED AND ON ANY THEORY OF
36
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
37
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
38
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39
 *
40
 * @Author: Hans Zandbelt - hans.zandbelt@openidc.com
41
 */
42
43
#include "metrics.h"
44
#include "mod_auth_openidc.h"
45
#include "proto/proto.h"
46
#include "util/util.h"
47
48
0
#define OIDC_PROTO_DPOP_JWT_TYP "dpop+jwt"
49
50
apr_byte_t oidc_proto_dpop_use_nonce(request_rec *r, const oidc_cfg_t *cfg, const oidc_json_t *j_result,
51
             apr_hash_t *response_hdrs, const char *url, const char *method,
52
0
             const char *access_token, char **dpop) {
53
0
  apr_byte_t rv = FALSE;
54
0
  const char *dpop_nonce = NULL;
55
56
0
  const oidc_json_t *j_error = oidc_json_object_get(j_result, OIDC_PROTO_ERROR);
57
0
  if ((j_error == NULL) || (!oidc_json_is_string(j_error)) ||
58
0
      (_oidc_strcmp(oidc_json_string_value(j_error), OIDC_PROTO_DPOP_USE_NONCE) != 0))
59
0
    goto end;
60
61
  /* try again with a DPoP nonce provided by the server */
62
0
  dpop_nonce = (char *)apr_hash_get(response_hdrs, OIDC_HTTP_HDR_DPOP_NONCE, APR_HASH_KEY_STRING);
63
0
  if (dpop_nonce == NULL) {
64
0
    oidc_error(r, "error is \"%s\" but no \"%s\" header found", OIDC_PROTO_DPOP_USE_NONCE,
65
0
         OIDC_HTTP_HDR_DPOP_NONCE);
66
0
    goto end;
67
0
  }
68
69
0
  rv = oidc_proto_dpop_create(r, cfg, url, method, access_token, dpop_nonce, dpop);
70
71
0
  if (rv == TRUE)
72
0
    OIDC_METRICS_COUNTER_INC(r, cfg, OM_PROVIDER_DPOP_RETRY);
73
74
0
end:
75
76
0
  oidc_debug(r, "leave: %d, dpop=%s", rv, *dpop ? "true" : "false");
77
78
0
  return rv;
79
0
}
80
81
/*
82
 * generate a DPoP proof for the specified URL/method/access_token
83
 */
84
apr_byte_t oidc_proto_dpop_create(request_rec *r, const oidc_cfg_t *cfg, const char *url, const char *method,
85
0
          const char *access_token, const char *nonce, char **dpop) {
86
0
  apr_byte_t rv = FALSE;
87
0
  oidc_jwt_t *jwt = NULL;
88
0
  oidc_jwk_t *jwk = NULL;
89
0
  oidc_jose_error_t err;
90
0
  char *s_jwk = NULL;
91
0
  char *ath = NULL;
92
93
0
  oidc_debug(r, "enter");
94
95
0
  if (oidc_proto_jwt_create_from_first_pkey(r, cfg, &jwk, &jwt, TRUE) == FALSE)
96
0
    goto end;
97
98
0
  oidc_json_object_set_new(jwt->header.value.json, OIDC_CLAIM_TYP, oidc_json_string(OIDC_PROTO_DPOP_JWT_TYP));
99
0
  if (oidc_jwk_to_public_json(r->pool, jwk, &s_jwk, &err) == FALSE) {
100
0
    oidc_error(r, "oidc_jwk_to_public_json failed: %s", oidc_jose_e2s(r->pool, err));
101
0
    goto end;
102
0
  }
103
0
  if (oidc_jwt_hdr_set_json(jwt, OIDC_CLAIM_JWK, s_jwk, &err) == FALSE) {
104
0
    oidc_error(r, "oidc_jwt_hdr_set_json failed: %s", oidc_jose_e2s(r->pool, err));
105
0
    goto end;
106
0
  }
107
108
0
  oidc_json_object_set_new(jwt->payload.value.json, OIDC_CLAIM_JTI, oidc_json_string(oidc_proto_jti_gen(r)));
109
0
  oidc_json_object_set_new(jwt->payload.value.json, OIDC_CLAIM_HTM, oidc_json_string(method));
110
0
  oidc_json_object_set_new(jwt->payload.value.json, OIDC_CLAIM_HTU, oidc_json_string(url));
111
0
  oidc_json_object_set_new(jwt->payload.value.json, OIDC_CLAIM_IAT,
112
0
         oidc_json_integer(apr_time_sec(apr_time_now())));
113
114
0
  if (access_token != NULL) {
115
0
    if (oidc_jose_hash_and_base64url_encode(r->pool, OIDC_JOSE_ALG_SHA256, access_token,
116
0
              (int)_oidc_strlen(access_token), &ath, &err) == FALSE) {
117
0
      oidc_error(r, "oidc_jose_hash_and_base64url_encode failed: %s", oidc_jose_e2s(r->pool, err));
118
0
      goto end;
119
0
    }
120
0
    oidc_json_object_set_new(jwt->payload.value.json, OIDC_CLAIM_ATH, oidc_json_string(ath));
121
0
  }
122
123
0
  if (nonce != NULL)
124
0
    oidc_json_object_set_new(jwt->payload.value.json, OIDC_CLAIM_NONCE, oidc_json_string(nonce));
125
126
0
  if (oidc_proto_jwt_sign_and_serialize(r, jwk, jwt, dpop) == FALSE)
127
0
    goto end;
128
129
0
  rv = TRUE;
130
131
0
end:
132
133
0
  if (jwt)
134
0
    oidc_jwt_destroy(jwt);
135
136
0
  return rv;
137
0
}