2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
13 * This program 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.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27 #include "namespaces.hh"
29 #include "dnsparser.hh"
30 #include "dnspacket.hh"
31 #include "dnsrecords.hh"
34 #include "arguments.hh"
36 #include <boost/shared_ptr.hpp>
37 #include <boost/make_shared.hpp>
38 #include "gss_context.hh"
40 #ifndef ENABLE_GSS_TSIG
42 bool GssContext::supported() { return false; }
43 GssContext::GssContext() { d_error
= GSS_CONTEXT_UNSUPPORTED
; d_type
= GSS_CONTEXT_NONE
; }
44 GssContext::GssContext(const DNSName
& label
) { d_error
= GSS_CONTEXT_UNSUPPORTED
; d_type
= GSS_CONTEXT_NONE
; }
45 void GssContext::setLocalPrincipal(const std::string
& name
) {}
46 bool GssContext::getLocalPrincipal(std::string
& name
) { return false; }
47 void GssContext::setPeerPrincipal(const std::string
& name
) {}
48 bool GssContext::getPeerPrincipal(std::string
& name
) { return false; }
49 void GssContext::generateLabel(const std::string
& suffix
) {}
50 void GssContext::setLabel(const DNSName
& label
) {}
51 bool GssContext::init(const std::string
&input
, std::string
& output
) { return false; }
52 bool GssContext::accept(const std::string
&input
, std::string
& output
) { return false; }
53 bool GssContext::destroy() { return false; }
54 bool GssContext::expired() { return false; }
55 bool GssContext::valid() { return false; }
56 bool GssContext::sign(const std::string
&input
, std::string
& output
) { return false; }
57 bool GssContext::verify(const std::string
&input
, const std::string
&signature
) { return false; }
58 GssContextError
GssContext::getError() { return GSS_CONTEXT_UNSUPPORTED
; }
62 class GssCredential
: boost::noncopyable
{
64 GssCredential(const std::string
& name
, const gss_cred_usage_t usage
) {
65 gss_buffer_desc buffer
;
66 d_name
= GSS_C_NO_NAME
;
68 d_cred
= GSS_C_NO_CREDENTIAL
;
73 if (name
.empty() == false) {
74 buffer
.length
= name
.size();
75 buffer
.value
= (void*)name
.c_str();
76 d_maj
= gss_import_name(&d_min
, &buffer
, (gss_OID
)GSS_KRB5_NT_PRINCIPAL_NAME
, &d_name
);
77 if (d_maj
!= GSS_S_COMPLETE
) {
87 OM_uint32 tmp_maj
__attribute__((unused
)), tmp_min
__attribute__((unused
));
88 if (d_cred
!= GSS_C_NO_CREDENTIAL
)
89 tmp_maj
= gss_release_cred(&tmp_min
, &d_cred
);
90 if (d_name
!= GSS_C_NO_NAME
)
91 tmp_maj
= gss_release_name(&tmp_min
, &d_name
);
94 bool expired() const {
95 if (d_expires
== -1) return false;
96 return time((time_t*)NULL
)>d_expires
;
100 OM_uint32 time_rec
, tmp_maj
__attribute__((unused
)), tmp_min
__attribute__((unused
));
101 d_maj
= gss_acquire_cred(&d_min
, d_name
, GSS_C_INDEFINITE
, GSS_C_NO_OID_SET
, d_usage
, &d_cred
, NULL
, &time_rec
);
103 if (d_maj
!= GSS_S_COMPLETE
) {
105 tmp_maj
= gss_release_name(&tmp_min
, &d_name
);
106 d_name
= GSS_C_NO_NAME
;
112 if (time_rec
> GSS_C_INDEFINITE
) {
113 d_expires
= time((time_t*)NULL
)+time_rec
;
122 return d_valid
&& !expired();
125 OM_uint32 d_maj
,d_min
;
131 gss_cred_id_t d_cred
;
132 gss_cred_usage_t d_usage
;
135 std::map
<std::string
, boost::shared_ptr
<GssCredential
> > s_gss_accept_creds
;
136 std::map
<std::string
, boost::shared_ptr
<GssCredential
> > s_gss_init_creds
;
138 class GssSecContext
: boost::noncopyable
{
140 GssSecContext(boost::shared_ptr
<GssCredential
> cred
) {
141 if (cred
->valid() == false) throw PDNSException("Invalid credential " + cred
->d_nameS
);
143 d_state
= GssStateInitial
;
144 d_ctx
= GSS_C_NO_CONTEXT
;
147 d_peer_name
= GSS_C_NO_NAME
;
148 d_type
= GSS_CONTEXT_NONE
;
152 OM_uint32 tmp_maj
__attribute__((unused
)), tmp_min
__attribute__((unused
));
153 if (d_ctx
!= GSS_C_NO_CONTEXT
) {
154 tmp_maj
= gss_delete_sec_context(&tmp_min
, &d_ctx
, GSS_C_NO_BUFFER
);
156 if (d_peer_name
!= GSS_C_NO_NAME
) {
157 tmp_maj
= gss_release_name(&tmp_min
, &(d_peer_name
));
161 GssContextType d_type
;
163 gss_name_t d_peer_name
;
165 boost::shared_ptr
<GssCredential
> d_cred
;
166 OM_uint32 d_maj
,d_min
;
177 std::map
<DNSName
, boost::shared_ptr
<GssSecContext
> > s_gss_sec_context
;
179 bool GssContext::supported() { return true; }
181 void GssContext::initialize() {
182 d_peerPrincipal
= "";
183 d_localPrincipal
= "";
184 d_error
= GSS_CONTEXT_NO_ERROR
;
185 d_type
= GSS_CONTEXT_NONE
;
188 GssContext::GssContext() {
190 generateLabel("pdns.tsig.");
193 GssContext::GssContext(const DNSName
& label
) {
198 void GssContext::generateLabel(const std::string
& suffix
) {
199 std::ostringstream oss
;
200 oss
<< std::hex
<< time((time_t*)NULL
) << "." << suffix
;
201 setLabel(DNSName(oss
.str()));
204 void GssContext::setLabel(const DNSName
& label
) {
206 if (s_gss_sec_context
.find(d_label
) != s_gss_sec_context
.end()) {
207 d_ctx
= s_gss_sec_context
[d_label
];
208 d_type
= d_ctx
->d_type
;
212 bool GssContext::expired() {
213 return (!d_ctx
|| (d_ctx
->d_expires
> -1 && d_ctx
->d_expires
< time((time_t*)NULL
)));
216 bool GssContext::valid() {
217 return (d_ctx
&& !expired() && d_ctx
->d_state
== GssSecContext::GssStateComplete
);
220 bool GssContext::init(const std::string
&input
, std::string
& output
) {
221 OM_uint32 tmp_maj
__attribute__((unused
)), tmp_min
__attribute__((unused
));
223 gss_buffer_desc recv_tok
, send_tok
, buffer
;
227 boost::shared_ptr
<GssCredential
> cred
;
228 if (d_label
.empty()) {
229 d_error
= GSS_CONTEXT_INVALID
;
233 d_type
= GSS_CONTEXT_INIT
;
235 if (s_gss_init_creds
.find(d_localPrincipal
) != s_gss_init_creds
.end()) {
236 cred
= s_gss_init_creds
[d_localPrincipal
];
238 s_gss_init_creds
[d_localPrincipal
] = boost::make_shared
<GssCredential
>(d_localPrincipal
, GSS_C_INITIATE
);
239 cred
= s_gss_init_creds
[d_localPrincipal
];
242 // see if we can find a context in non-completed state
244 if (d_ctx
->d_state
!= GssSecContext::GssStateNegotiate
) {
245 d_error
= GSS_CONTEXT_INVALID
;
250 s_gss_sec_context
[d_label
] = boost::make_shared
<GssSecContext
>(cred
);
251 s_gss_sec_context
[d_label
]->d_type
= d_type
;
252 d_ctx
= s_gss_sec_context
[d_label
];
253 d_ctx
->d_state
= GssSecContext::GssStateNegotiate
;
256 recv_tok
.length
= input
.size();
257 recv_tok
.value
= (void*)input
.c_str();
259 if (d_peerPrincipal
.empty() == false) {
260 buffer
.value
= (void*)d_peerPrincipal
.c_str();
261 buffer
.length
= d_peerPrincipal
.size();
262 maj
= gss_import_name(&min
, &buffer
, (gss_OID
)GSS_KRB5_NT_PRINCIPAL_NAME
, &(d_ctx
->d_peer_name
));
263 if (maj
!= GSS_S_COMPLETE
) {
264 processError("gss_import_name", maj
, min
);
269 maj
= gss_init_sec_context(&min
, cred
->d_cred
, &(d_ctx
->d_ctx
), d_ctx
->d_peer_name
, GSS_C_NO_OID
, GSS_C_MUTUAL_FLAG
|GSS_C_REPLAY_FLAG
, GSS_C_INDEFINITE
, GSS_C_NO_CHANNEL_BINDINGS
, &recv_tok
, NULL
, &send_tok
, &flags
, &expires
);
271 if (send_tok
.length
>0) {
272 output
.assign((const char*)send_tok
.value
, send_tok
.length
);
273 tmp_maj
= gss_release_buffer(&tmp_min
, &send_tok
);
276 if (maj
== GSS_S_COMPLETE
) {
277 if (expires
> GSS_C_INDEFINITE
) {
278 d_ctx
->d_expires
= time((time_t*)NULL
) + expires
;
280 d_ctx
->d_expires
= -1;
282 d_ctx
->d_state
= GssSecContext::GssStateComplete
;
284 } else if (maj
!= GSS_S_CONTINUE_NEEDED
) {
285 processError("gss_init_sec_context", maj
,min
);
288 return (maj
== GSS_S_CONTINUE_NEEDED
);
291 bool GssContext::accept(const std::string
&input
, std::string
& output
) {
292 OM_uint32 tmp_maj
__attribute__((unused
)), tmp_min
__attribute__((unused
));
294 gss_buffer_desc recv_tok
, send_tok
;
298 boost::shared_ptr
<GssCredential
> cred
;
299 if (d_label
.empty()) {
300 d_error
= GSS_CONTEXT_INVALID
;
304 d_type
= GSS_CONTEXT_ACCEPT
;
306 if (s_gss_accept_creds
.find(d_localPrincipal
) != s_gss_accept_creds
.end()) {
307 cred
= s_gss_accept_creds
[d_localPrincipal
];
309 s_gss_accept_creds
[d_localPrincipal
] = boost::make_shared
<GssCredential
>(d_localPrincipal
, GSS_C_ACCEPT
);
310 cred
= s_gss_accept_creds
[d_localPrincipal
];
313 // see if we can find a context in non-completed state
315 if (d_ctx
->d_state
!= GssSecContext::GssStateNegotiate
) {
316 d_error
= GSS_CONTEXT_INVALID
;
321 s_gss_sec_context
[d_label
] = boost::make_shared
<GssSecContext
>(cred
);
322 s_gss_sec_context
[d_label
]->d_type
= d_type
;
323 d_ctx
= s_gss_sec_context
[d_label
];
324 d_ctx
->d_state
= GssSecContext::GssStateNegotiate
;
327 recv_tok
.length
= input
.size();
328 recv_tok
.value
= (void*)input
.c_str();
330 maj
= gss_accept_sec_context(&min
, &(d_ctx
->d_ctx
), cred
->d_cred
, &recv_tok
, GSS_C_NO_CHANNEL_BINDINGS
, &(d_ctx
->d_peer_name
), NULL
, &send_tok
, &flags
, &expires
, NULL
);
332 if (send_tok
.length
>0) {
333 output
.assign((const char*)send_tok
.value
, send_tok
.length
);
334 tmp_maj
= gss_release_buffer(&tmp_min
, &send_tok
);
337 if (maj
== GSS_S_COMPLETE
) {
338 if (expires
> GSS_C_INDEFINITE
) {
339 d_ctx
->d_expires
= time((time_t*)NULL
) + expires
;
341 d_ctx
->d_expires
= -1;
343 d_ctx
->d_state
= GssSecContext::GssStateComplete
;
345 } else if (maj
!= GSS_S_CONTINUE_NEEDED
) {
346 processError("gss_accept_sec_context", maj
,min
);
348 return (maj
== GSS_S_CONTINUE_NEEDED
);
351 bool GssContext::sign(const std::string
& input
, std::string
& output
) {
352 OM_uint32 tmp_maj
__attribute__((unused
)), tmp_min
__attribute__((unused
));
355 gss_buffer_desc recv_tok
= GSS_C_EMPTY_BUFFER
;
356 gss_buffer_desc send_tok
= GSS_C_EMPTY_BUFFER
;
358 recv_tok
.length
= input
.size();
359 recv_tok
.value
= (void*)input
.c_str();
361 maj
= gss_get_mic(&min
, d_ctx
->d_ctx
, GSS_C_QOP_DEFAULT
, &recv_tok
, &send_tok
);
363 if (send_tok
.length
>0) {
364 output
.assign((const char*)send_tok
.value
, send_tok
.length
);
365 tmp_maj
= gss_release_buffer(&tmp_min
, &send_tok
);
368 if (maj
!= GSS_S_COMPLETE
) {
369 processError("gss_get_mic", maj
,min
);
372 return (maj
== GSS_S_COMPLETE
);
375 bool GssContext::verify(const std::string
& input
, const std::string
& signature
) {
378 gss_buffer_desc recv_tok
= GSS_C_EMPTY_BUFFER
;
379 gss_buffer_desc sign_tok
= GSS_C_EMPTY_BUFFER
;
381 recv_tok
.length
= input
.size();
382 recv_tok
.value
= (void*)input
.c_str();
383 sign_tok
.length
= signature
.size();
384 sign_tok
.value
= (void*)signature
.c_str();
386 maj
= gss_verify_mic(&min
, d_ctx
->d_ctx
, &recv_tok
, &sign_tok
, NULL
);
388 if (maj
!= GSS_S_COMPLETE
) {
389 processError("gss_get_mic", maj
,min
);
392 return (maj
== GSS_S_COMPLETE
);
395 bool GssContext::destroy() {
399 void GssContext::setLocalPrincipal(const std::string
& name
) {
400 d_localPrincipal
= name
;
403 bool GssContext::getLocalPrincipal(std::string
& name
) {
404 name
= d_localPrincipal
;
405 return name
.size()>0;
408 void GssContext::setPeerPrincipal(const std::string
& name
) {
409 d_peerPrincipal
= name
;
412 bool GssContext::getPeerPrincipal(std::string
& name
) {
413 gss_buffer_desc value
;
416 if (d_ctx
->d_peer_name
!= GSS_C_NO_NAME
) {
417 maj
= gss_display_name(&min
, d_ctx
->d_peer_name
, &value
, NULL
);
418 if (maj
== GSS_S_COMPLETE
&& value
.length
> 0) {
419 name
.assign((const char*)value
.value
, value
.length
);
420 maj
= gss_release_buffer(&min
, &value
);
430 void GssContext::processError(const std::string
& method
, OM_uint32 maj
, OM_uint32 min
) {
438 gss_display_status(&tmp_min
, maj
, GSS_C_GSS_CODE
, GSS_C_NULL_OID
, &msg_ctx
, &msg
);
439 oss
<< method
<< ": " << (char*)msg
.value
;
440 d_gss_errors
.push_back(oss
.str());
446 gss_display_status(&tmp_min
, min
, GSS_C_MECH_CODE
, GSS_C_NULL_OID
, &msg_ctx
, &msg
);
447 oss
<< method
<< ": " << (char*)msg
.value
;
448 d_gss_errors
.push_back(oss
.str());
455 bool gss_add_signature(const DNSName
& context
, const std::string
& message
, std::string
& mac
) {
457 GssContext
gssctx(context
);
458 if (!gssctx
.valid()) {
459 L
<<Logger::Error
<<"GSS context '"<<context
<<"' is not valid"<<endl
;
460 for(const string
& error
: gssctx
.getErrorStrings()) {
461 L
<<Logger::Error
<<"GSS error: "<<error
<<endl
;;
466 if (!gssctx
.sign(message
, tmp_mac
)) {
467 L
<<Logger::Error
<<"Could not sign message using GSS context '"<<context
<<"'"<<endl
;
468 for(const string
& error
: gssctx
.getErrorStrings()) {
469 L
<<Logger::Error
<<"GSS error: "<<error
<<endl
;;
477 bool gss_verify_signature(const DNSName
& context
, const std::string
& message
, const std::string
& mac
) {
478 GssContext
gssctx(context
);
479 if (!gssctx
.valid()) {
480 L
<<Logger::Error
<<"GSS context '"<<context
<<"' is not valid"<<endl
;
481 for(const string
& error
: gssctx
.getErrorStrings()) {
482 L
<<Logger::Error
<<"GSS error: "<<error
<<endl
;;
487 if (!gssctx
.verify(message
, mac
)) {
488 L
<<Logger::Error
<<"Could not verify message using GSS context '"<<context
<<"'"<<endl
;
489 for(const string
& error
: gssctx
.getErrorStrings()) {
490 L
<<Logger::Error
<<"GSS error: "<<error
<<endl
;;