XRootD
Loading...
Searching...
No Matches
XrdHttpSecurity.cc
Go to the documentation of this file.
1//------------------------------------------------------------------------------
2// This file is part of XrdHTTP: A pragmatic implementation of the
3// HTTP/WebDAV protocol for the Xrootd framework
4//
5// Copyright (c) 2020 by European Organization for Nuclear Research (CERN)
6// Author: Fabrizio Furano <furano@cern.ch>
7//------------------------------------------------------------------------------
8// XRootD is free software: you can redistribute it and/or modify
9// it under the terms of the GNU Lesser General Public License as published by
10// the Free Software Foundation, either version 3 of the License, or
11// (at your option) any later version.
12//
13// XRootD is distributed in the hope that it will be useful,
14// but WITHOUT ANY WARRANTY; without even the implied warranty of
15// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16// GNU General Public License for more details.
17//
18// You should have received a copy of the GNU Lesser General Public License
19// along with XRootD. If not, see <http://www.gnu.org/licenses/>.
20//------------------------------------------------------------------------------
21
22#include "XrdHttpProtocol.hh"
23#include "XrdHttpTrace.hh"
24#include "XrdHttpSecXtractor.hh"
25#include "Xrd/XrdLink.hh"
32#include "XrdOuc/XrdOucGMap.hh"
33
34namespace XrdHttpProtoInfo
35{
36 extern XrdTlsContext *xrdctx;
37}
38
39XrdOucGMap *XrdHttpProtocol::servGMap = 0; // Grid mapping service
40XrdCryptoFactory *XrdHttpProtocol::myCryptoFactory = 0;
41
42// Static definitions
43#define TRACELINK lp
44
45namespace
46{
47const char *TraceID = "Security";
48}
49
50using namespace XrdHttpProtoInfo;
51
52/******************************************************************************/
53/* I n i t S e c u r i t y */
54/******************************************************************************/
55
56bool XrdHttpProtocol::InitSecurity() {
57 // Borrow the initialization of XrdCryptossl, in order to share the
58 // OpenSSL threading bits
59 if (!(myCryptoFactory = XrdCryptoFactory::GetCryptoFactory("ssl"))) {
60 eDest.Say("Error instantiating crypto factory ssl", "");
61 return false;
62 }
63
64// If GRID map file was specified, load the plugin for it
65//
66 if (gridmap) {
67 XrdOucString pars;
68 if (XrdHttpTrace.What & TRACE_DEBUG) pars += "dbg|";
69
70 if (!(servGMap = XrdOucgetGMap(&eDest, gridmap, pars.c_str()))) {
71 eDest.Say("Error loading grid map file:", gridmap);
72 return false;
73 }
74 TRACE(ALL, "using grid map file: "<< gridmap);
75 }
76
77// If a secxtractor was specified, load that too.
78//
79 if (secxtractor)
80 {SSL_CTX *sslctx = (SSL_CTX*)xrdctx->Context(); // Need to avoid this!
81 secxtractor->Init(sslctx, XrdHttpTrace.What);
82 }
83
84// All done
85//
86 return true;
87}
88
89/******************************************************************************/
90/* H a n d l e A u t h e n t i c a t i o n */
91/******************************************************************************/
92
93int
94XrdHttpProtocol::HandleAuthentication(XrdLink* lp)
95{
96 EPNAME("HandleAuthentication");
97 int rc_ssl = SSL_get_verify_result(ssl);
98
99 if (rc_ssl) {
100 TRACEI(DEBUG, " SSL_get_verify_result returned :" << rc_ssl);
101 return 1;
102 }
103
104 XrdTlsPeerCerts pc(SSL_get_peer_certificate(ssl),SSL_get_peer_cert_chain(ssl));
105 XrdCryptoX509Chain chain;
106
107 if ((!pc.hasCert()) ||
108 (myCryptoFactory && !myCryptoFactory->X509ParseStack()(&pc, &chain))) {
109 TRACEI(DEBUG, "No certificate found in peer chain.");
110 chain.Cleanup();
111 return 0;
112 }
113
114 // Extract the DN for the current connection that will be used later on when
115 // handling the gridmap file
116 const char * dn = chain.EECname();
117 const char * eechash = chain.EEChash();
118
119 if (!dn || !eechash) {
120 // X509Chain doesn't assume it owns the underlying certs unless
121 // you explicitly invoke the Cleanup method
122 TRACEI(DEBUG, "Failed to extract DN information.");
123 chain.Cleanup();
124 return 1;
125 }
126
127 if (SecEntity.moninfo) {
128 free(SecEntity.moninfo);
129 }
130
131 SecEntity.moninfo = strdup(dn);
132 TRACEI(DEBUG, " Subject name is : '" << SecEntity.moninfo << "'; hash is " << eechash);
133 // X509Chain doesn't assume it owns the underlying certs unless
134 // you explicitly invoke the Cleanup method
135
136 if (GetVOMSData(lp)) {
137 TRACEI(DEBUG, " No VOMS information for DN: " << SecEntity.moninfo);
138
139 if (isRequiredXtractor) {
140 eDest.Emsg(epname, "Failed extracting required VOMS info for DN: ",
141 SecEntity.moninfo);
142 chain.Cleanup();
143 return 1;
144 }
145 }
146
147 auto retval = HandleGridMap(lp, eechash);
148 chain.Cleanup();
149 return retval;
150}
151
152
153/******************************************************************************/
154/* H a n d l e G r i d M a p */
155/******************************************************************************/
156
157int
158XrdHttpProtocol::HandleGridMap(XrdLink* lp, const char * eechash)
159{
160 EPNAME("HandleGridMap");
161 char bufname[256];
162
163 if (servGMap) {
164 int mape = servGMap->dn2user(SecEntity.moninfo, bufname, sizeof(bufname), 0);
165 if ( !mape && SecEntity.moninfo[0] ) {
166 TRACEI(DEBUG, " Mapping name: '" << SecEntity.moninfo << "' --> " << bufname);
167 if (SecEntity.name) free(SecEntity.name);
168 SecEntity.name = strdup(bufname);
169 SecEntity.eaAPI->Add("gridmap.name", "1", true);
170 }
171 else {
172 TRACEI(ALL, " Mapping name: " << SecEntity.moninfo << " Failed. err: " << mape);
173
174 if (isRequiredGridmap) {
175 eDest.Emsg(epname, "Required gridmap mapping failed for DN:",
176 SecEntity.moninfo);
177 return 1;
178 }
179 }
180 }
181
182 if (!SecEntity.name && !compatNameGeneration) {
183 TRACEI(DEBUG, " Will fallback name to subject hash: " << eechash);
184 SecEntity.name = strdup(eechash);
185 return 0;
186 }
187
188 if (!SecEntity.name) {
189 // Here we have the user DN, and try to extract an useful user name from it
190 if (SecEntity.name) free(SecEntity.name);
191 SecEntity.name = 0;
192 // To set the name we pick the first CN of the certificate subject
193 // and hope that it makes some sense, it usually does
194 char *lnpos = strstr(SecEntity.moninfo, "/CN=");
195 char bufname2[9];
196
197
198 if (lnpos) {
199 lnpos += 4;
200 char *lnpos2 = index(lnpos, '/');
201 if (lnpos2) {
202 int l = ( lnpos2-lnpos < (int)sizeof(bufname) ? lnpos2-lnpos : (int)sizeof(bufname)-1 );
203 strncpy(bufname, lnpos, l);
204 bufname[l] = '\0';
205
206 // Here we have the string in the buffer. Take the last 8 non-space characters
207 size_t j = 8;
208 strcpy(bufname2, "unknown-"); // note it's 8 chars + '\0' at the end
209 for (int i = (int)strlen(bufname)-1; i >= 0; i--) {
210 if (isalnum(bufname[i])) {
211 j--;
212 bufname2[j] = bufname[i];
213 if (j == 0) break;
214 }
215
216 }
217
218 SecEntity.name = strdup(bufname);
219 TRACEI(DEBUG, " Setting link name: '" << bufname2+j << "'");
220 lp->setID(bufname2+j, 0);
221 }
222 }
223 }
224
225 // If we could not find anything good, take the last 8 non-space characters of the main subject
226 if (!SecEntity.name) {
227 size_t j = 8;
228 SecEntity.name = strdup("unknown-\0"); // note it's 9 chars
229 for (int i = (int)strlen(SecEntity.moninfo)-1; i >= 0; i--) {
230 if (isalnum(SecEntity.moninfo[i])) {
231 j--;
232 SecEntity.name[j] = SecEntity.moninfo[i];
233 if (j == 0) break;
234 }
235 }
236 }
237
238 return 0;
239}
240
241
242/******************************************************************************/
243/* G e t V O M S D a t a */
244/******************************************************************************/
245
246int XrdHttpProtocol::GetVOMSData(XrdLink *lp)
247{
248 TRACEI(DEBUG, " Extracting auth info.");
249
250 // Invoke the Security exctractor plugin which will fill in the XrdSecEntity
251 // with VOMS info, if VOMS is installed. If we have no sec extractor then do
252 // nothing, just plain https will work.
253 if (secxtractor) {
254 // Note: this is kept for compatibility with XrdHttpVOMS which modified the
255 // SecEntity.name filed
256 char *savestr = 0;
257
258 if (servGMap && SecEntity.name) {
259 savestr = strdup(SecEntity.name);
260 }
261
262 int r = secxtractor->GetSecData(lp, SecEntity, ssl);
263
264 if (servGMap && savestr) {
265 if (SecEntity.name) free(SecEntity.name);
266 SecEntity.name = savestr;
267 }
268
269 if (r) {
270 TRACEI(ALL, " Certificate data extraction failed: " << SecEntity.moninfo
271 << " Failed. err: " << r);
272 }
273
274 return r;
275 }
276
277 return 0;
278}
#define DEBUG(x)
#define EPNAME(x)
XrdSysTrace XrdHttpTrace("http")
A pragmatic implementation of the HTTP/DAV protocol for the Xrd framework.
Trace definitions.
XrdOucGMap * XrdOucgetGMap(XrdOucGMapArgs)
Definition XrdOucGMap.cc:92
#define TRACE_DEBUG
Definition XrdTrace.hh:36
#define TRACE(act, x)
Definition XrdTrace.hh:63
#define TRACEI(act, x)
Definition XrdTrace.hh:66
static XrdCryptoFactory * GetCryptoFactory(const char *factoryname)
void Cleanup(bool keepCA=0)
static char * gridmap
Gridmap file location. The same used by XrdSecGsi.
static XrdSysError eDest
static XrdOucGMap * servGMap
The instance of the DN mapper. Created only when a valid path is given.
static bool compatNameGeneration
static bool isRequiredGridmap
XrdSecEntity SecEntity
Authentication area.
const char * c_str() const
XrdTlsContext * xrdctx