blob: e189f2e34f6f5d0db5f86ead475ff9216ca82866 [file] [log] [blame]
Nico Huberee52fbc2023-06-24 11:52:57 +00001<?php
2/*
3 * SimpleID
4 *
5 * Copyright (C) Kelvin Mo 2009
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public
18 * License along with this program; if not, write to the Free
19 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 *
21 * $Id$
22 */
23
24/**
25 * Implements the Provider Authentication Policy Extension extension.
26 *
27 *
28 * @package simpleid
29 * @subpackage extensions
30 * @filesource
31 */
32
33/** Namespace for the PAPE extension */
34define('OPENID_NS_PAPE', 'http://specs.openid.net/extensions/pape/1.0');
35
36/** Namespaces for PAPE policies */
37define('PAPE_POLICY_NONE', 'http://schemas.openid.net/pape/policies/2007/06/none');
38define('PAPE_POLICY_PPID', 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifier');
39
40/** Namespaces for PAPE levels */
41define('PAPE_LEVEL_NIST800_63', 'http://csrc.nist.gov/publications/nistpubs/800-63/SP800-63V1_0_2.pdf');
42
43/**
44 * Returns the support for PAPE in SimpleID XRDS document
45 *
46 * @return array
47 * @see hook_xrds_types()
48 */
49function pape_xrds_types() {
50 return array(
51 OPENID_NS_PAPE,
52 PAPE_POLICY_PPID,
53 PAPE_LEVEL_NIST800_63
54 );
55}
56
57/**
58 * @see hook_checkid_identity()
59 */
60function pape_checkid_identity($request, $identity, $immediate) {
61 global $user;
62
63 // We only respond if the extension is requested
64 if (!openid_extension_requested(OPENID_NS_PAPE, $request)) return null;
65
66 // See if we are choosing an identity and save for later
67 // This may be used by pape_response() to produce a private identifier
68 if ($request['openid.identity'] == OPENID_IDENTIFIER_SELECT) _pape_identifier_select(true);
69
70 $pape_request = openid_extension_filter_request(OPENID_NS_PAPE, $request);
71
72 // If the relying party provides a max_auth_age
73 if (isset($pape_request['max_auth_age'])) {
74 // If we are not logged in then we don't need to do anything
75 if ($user == NULL) return NULL;
76
77 // If the last time we logged on actively (i.e. using a password) is greater than
78 // max_auth_age, we then require the user to log in again
79 if ((!isset($user['auth_active']) || !$user['auth_active'])
80 && ((time() - $user['auth_time']) > $pape_request['max_auth_age'])) {
81 set_message(t('This web site\'s policy requires you to log in again to confirm your identity.'));
82
83 _user_logout();
84 return CHECKID_LOGIN_REQUIRED;
85 }
86 }
87}
88
89/**
90 * @see hook_response()
91 */
92function pape_response($assertion, $request) {
93 global $user, $version;
94
95 // We only deal with positive assertions
96 if (!$assertion) return array();
97
98 // We only respond if we are using OpenID 2 or later
99 if ($version < OPENID_VERSION_2) return array();
100
101 // Get what is requested
102 $pape_request = openid_extension_filter_request(OPENID_NS_PAPE, $request);
103
104 // If the extension is requested, we use the same alias, otherwise, we
105 // make one up
106 $alias = openid_extension_alias(OPENID_NS_PAPE, 'pape');
107 $response = array();
108
109 // The PAPE specification recommends us to respond even when the extension
110 // is not present in the request.
111 $response['openid.ns.' . $alias] = OPENID_NS_PAPE;
112
113 // We return the last time the user logged in using the login form
114 $response['openid.' . $alias . '.auth_time'] = gmstrftime('%Y-%m-%dT%H:%M:%SZ', $user['auth_time']);
115
116 // We don't comply with NIST_SP800-63
117 $response['openid.' . $alias . '.auth_level.ns.nist'] = PAPE_LEVEL_NIST800_63;
118 $response['openid.' . $alias . '.auth_level.nist'] = 0;
119
120 // The default is that we don't apply any authentication policies. This can be changed later in the
121 // function
122 $response['openid.' . $alias . '.auth_policies'] = PAPE_POLICY_NONE;
123
124 // Now we go through the authentication policies
125 if (isset($pape_request['preferred_auth_policies'])) {
126 $policies = preg_split('/\s+/', $pape_request['preferred_auth_policies']);
127
128 if (in_array(PAPE_POLICY_PPID, $policies)) {
129 // We want a ppid. Check that the authentication request is correct
130 if (_pape_identifier_select()) {
131 $realm = openid_get_realm($request, $version);
132 $identity = $request['openid.identity'];
133
134 $ppid = _pape_ppid($identity, $realm);
135 $response['openid.claimed_id'] = $ppid;
136 $response['openid.identity'] = $ppid;
137 }
138 }
139 }
140
141 return $response;
142}
143
144/**
145 * Returns an array of fields that need signing.
146 *
147 * @see hook_signed_fields()
148 */
149function pape_signed_fields($response) {
150 $fields = array_keys(openid_extension_filter_request(OPENID_NS_PAPE, $response));
151 $alias = openid_extension_alias(OPENID_NS_PAPE);
152 $signed_fields = array();
153
154 if (isset($response['openid.ns.' . $alias])) $signed_fields[] = 'ns.' . $alias;
155 foreach ($fields as $field) {
156 if (isset($response['openid.' . $alias . '.' . $field])) $signed_fields[] = $alias . '.' . $field;
157 }
158
159 return $signed_fields;
160}
161
162/**
163 * Sets and returns whether the current OpenID request is requesting an identity.
164 *
165 * @param bool $identifier_select
166 * @return bool whether the current OpenID request is requesting an identity
167 */
168function _pape_identifier_select($identifier_select = NULL) {
169 static $static_identifier_select = false;
170
171 if (!is_null($identifier_select)) $static_identifier_select = $identifier_select;
172
173 return $static_identifier_select;
174}
175
176/**
177 * Generates a private personal identifier (PPID). The PPID is an opaque identifier
178 * for a particular user-RP pair
179 *
180 * @param string $identity the identity of the user
181 * @param string $realm the URL of the relying party
182 * @return string the PPID
183 */
184function _pape_ppid($identity, $realm) {
185 // We are reusing the site-token from get_form_token() in common.inc
186 if (store_get('site-token') == NULL) {
187 $site_token = mt_rand();
188 store_set('site-token', $site_token);
189 } else {
190 $site_token = store_get('site-token');
191 }
192
193 $parts = parse_url($realm);
194 $host = $parts['host'];
195 if (strstr($host, 'www.') === 0) $host = substr($host, 4);
196
197 return simpleid_url('ppid/' . md5($site_token . $identity . $host));
198}
199
200?>