1
- module Thrift
2
- class SaslClientTransport < BufferedTransport
3
- attr_reader :challenge
1
+ require 'gssapi'
4
2
3
+ module Thrift
4
+ class SaslClientTransport < FramedTransport
5
5
STATUS_BYTES = 1
6
6
PAYLOAD_LENGTH_BYTES = 4
7
- AUTH_MECHANISM = 'PLAIN'
8
7
NEGOTIATION_STATUS = {
9
8
START : 0x01 ,
10
9
OK : 0x02 ,
@@ -15,76 +14,122 @@ class SaslClientTransport < BufferedTransport
15
14
16
15
def initialize ( transport , sasl_params = { } )
17
16
super ( transport )
18
- @challenge = nil
17
+ @sasl_complete = nil
19
18
@sasl_username = sasl_params . fetch ( :username , 'anonymous' )
20
19
@sasl_password = sasl_params . fetch ( :password , 'anonymous' )
20
+ @sasl_mechanism = sasl_params . fetch ( :mechanism , 'PLAIN' )
21
+
22
+ unless [ 'PLAIN' , 'GSSAPI' ] . include? @sasl_mechanism
23
+ raise "Unknown SASL mechanism: #{ @sasl_mechanism } "
24
+ end
25
+
26
+ if @sasl_mechanism == 'GSSAPI'
27
+ @sasl_remote_principal = sasl_params [ :remote_principal ]
28
+ @sasl_remote_host = sasl_params [ :remote_host ]
29
+ @gsscli = GSSAPI ::Simple . new ( @sasl_remote_host , @sasl_remote_principal )
30
+ end
21
31
end
22
32
23
33
def read ( sz )
24
- len , = @transport . read ( PAYLOAD_LENGTH_BYTES ) . unpack ( 'l>' ) if @rbuf . nil?
25
- sz = len if len && sz > len
26
- @index += sz
27
- ret = @rbuf . slice ( @index - sz , sz ) || Bytes . empty_byte_buffer
28
- if ret . length == 0
29
- @rbuf = @transport . read ( len ) rescue Bytes . empty_byte_buffer
30
- @index = sz
31
- ret = @rbuf . slice ( 0 , sz ) || Bytes . empty_byte_buffer
32
- end
33
- ret
34
+ handshake! unless @sasl_complete
35
+ super ( sz )
34
36
end
35
37
36
38
def read_byte
37
- reset_buffer! if @index >= @rbuf . size
38
- @index += 1
39
- Bytes . get_string_byte ( @rbuf , @index - 1 )
39
+ handshake! unless @sasl_complete
40
+ super
40
41
end
41
42
42
- def read_into_buffer ( buffer , size )
43
- i = 0
44
- while i < size
45
- reset_buffer! if @index >= @rbuf . size
46
- byte = Bytes . get_string_byte ( @rbuf , @index )
47
- Bytes . set_string_byte ( buffer , i , byte )
48
- @index += 1
49
- i += 1
50
- end
51
- i
43
+ def read_into_buffer ( buf , size )
44
+ handshake! unless @sasl_complete
45
+ super ( buf , size )
52
46
end
53
47
54
48
def write ( buf )
55
- initiate_hand_shake if @challenge . nil?
56
- header = [ buf . length ] . pack ( 'l>' )
57
- @wbuf << ( header + Bytes . force_binary_encoding ( buf ) )
49
+ handshake! unless @sasl_complete
50
+ super ( buf )
58
51
end
59
52
60
- protected
53
+ def flush
54
+ handshake! unless @sasl_complete
55
+ super
56
+ end
61
57
62
- def initiate_hand_shake
63
- header = [ NEGOTIATION_STATUS [ :START ] , AUTH_MECHANISM . length ] . pack ( 'cl>' )
64
- @transport . write header + AUTH_MECHANISM
65
- message = "[#{ AUTH_MECHANISM } ]\u0000 #{ @sasl_username } \u0000 #{ @sasl_password } "
66
- header = [ NEGOTIATION_STATUS [ :OK ] , message . length ] . pack ( 'cl>' )
67
- @transport . write header + message
68
- status , len = @transport . read ( STATUS_BYTES + PAYLOAD_LENGTH_BYTES ) . unpack ( 'cl>' )
58
+ private
59
+
60
+ def handshake!
61
+ case @sasl_mechanism
62
+ when 'PLAIN'
63
+ handshake_plain!
64
+ when 'GSSAPI'
65
+ handshake_gssapi!
66
+ end
67
+ end
68
+
69
+ def handshake_plain!
70
+ token = "[#{ @sasl_mechanism } ]\u0000 #{ @sasl_username } \u0000 #{ @sasl_password } "
71
+ write_handshake_message ( NEGOTIATION_STATUS [ :START ] , @sasl_mechanism )
72
+ write_handshake_message ( NEGOTIATION_STATUS [ :OK ] , token )
73
+
74
+ status , msg = read_handshake_message
69
75
case status
70
- when NEGOTIATION_STATUS [ :BAD ] , NEGOTIATION_STATUS [ :ERROR ]
71
- raise @transport . to_io . read ( len )
72
76
when NEGOTIATION_STATUS [ :COMPLETE ]
73
- @challenge = @transport . to_io . read len
77
+ @sasl_complete = true
74
78
when NEGOTIATION_STATUS [ :OK ]
75
79
raise "Failed to complete challenge exchange: only NONE supported currently"
76
80
end
77
81
end
78
82
79
- private
83
+ def handshake_gssapi!
84
+ token = @gsscli . init_context
85
+ write_handshake_message ( NEGOTIATION_STATUS [ :START ] , @sasl_mechanism )
86
+ write_handshake_message ( NEGOTIATION_STATUS [ :OK ] , token )
87
+
88
+ status , msg = read_handshake_message
89
+ case status
90
+ when NEGOTIATION_STATUS [ :COMPLETE ]
91
+ raise "Unexpected COMPLETE from server"
92
+ when NEGOTIATION_STATUS [ :OK ]
93
+ unless @gsscli . init_context ( msg )
94
+ raise "GSSAPI: challenge provided by server could not be verified"
95
+ end
96
+
97
+ write_handshake_message ( NEGOTIATION_STATUS [ :OK ] , "" )
98
+
99
+ status , msg = read_handshake_message
100
+ case status
101
+ when NEGOTIATION_STATUS [ :COMPLETE ]
102
+ raise "Unexpected COMPLETE from server"
103
+ when NEGOTIATION_STATUS [ :OK ]
104
+ unwrapped = @gsscli . unwrap_message ( msg )
105
+ rewrapped = @gsscli . wrap_message ( unwrapped )
106
+
107
+ write_handshake_message ( NEGOTIATION_STATUS [ :COMPLETE ] , rewrapped )
80
108
81
- def reset_buffer!
82
- len , = @transport . read ( PAYLOAD_LENGTH_BYTES ) . unpack ( 'l>' )
83
- @rbuf = @transport . read ( len )
84
- while @rbuf . size < len
85
- @rbuf << @transport . read ( len - @rbuf . size )
109
+ status , msg = read_handshake_message
110
+ case status
111
+ when NEGOTIATION_STATUS [ :COMPLETE ]
112
+ @sasl_complete = true
113
+ when NEGOTIATION_STATUS [ :OK ]
114
+ raise "Failed to complete GSS challenge exchange"
115
+ end
116
+ end
86
117
end
87
- @index = 0
118
+ end
119
+
120
+ def read_handshake_message
121
+ status , len = @transport . read ( STATUS_BYTES + PAYLOAD_LENGTH_BYTES ) . unpack ( 'cl>' )
122
+ body = @transport . to_io . read ( len )
123
+ if [ NEGOTIATION_STATUS [ :BAD ] , NEGOTIATION_STATUS [ :ERROR ] ] . include? ( status )
124
+ raise "Exception from server: #{ body } "
125
+ end
126
+
127
+ [ status , body ]
128
+ end
129
+
130
+ def write_handshake_message ( status , message )
131
+ header = [ status , message . length ] . pack ( 'cl>' )
132
+ @transport . write ( header + message )
88
133
end
89
134
end
90
135
@@ -93,5 +138,4 @@ def get_transport(transport)
93
138
return SaslClientTransport . new ( transport )
94
139
end
95
140
end
96
-
97
141
end
0 commit comments