1// Copyright (C) MongoDB, Inc. 2022-present.
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may
4// not use this file except in compliance with the License. You may obtain
5// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
6
7//+build gssapi
8//+build linux darwin
9
10#include <string.h>
11#include <stdio.h>
12#include "gss_wrapper.h"
13
14OM_uint32 gssapi_canonicalize_name(
15 OM_uint32* minor_status,
16 char *input_name,
17 gss_OID input_name_type,
18 gss_name_t *output_name
19)
20{
21 OM_uint32 major_status;
22 gss_name_t imported_name = GSS_C_NO_NAME;
23 gss_buffer_desc buffer = GSS_C_EMPTY_BUFFER;
24
25 buffer.value = input_name;
26 buffer.length = strlen(input_name);
27 major_status = gss_import_name(minor_status, &buffer, input_name_type, &imported_name);
28 if (GSS_ERROR(major_status)) {
29 return major_status;
30 }
31
32 major_status = gss_canonicalize_name(minor_status, imported_name, (gss_OID)gss_mech_krb5, output_name);
33 if (imported_name != GSS_C_NO_NAME) {
34 OM_uint32 ignored;
35 gss_release_name(&ignored, &imported_name);
36 }
37
38 return major_status;
39}
40
41int gssapi_error_desc(
42 OM_uint32 maj_stat,
43 OM_uint32 min_stat,
44 char **desc
45)
46{
47 OM_uint32 stat = maj_stat;
48 int stat_type = GSS_C_GSS_CODE;
49 if (min_stat != 0) {
50 stat = min_stat;
51 stat_type = GSS_C_MECH_CODE;
52 }
53
54 OM_uint32 local_maj_stat, local_min_stat;
55 OM_uint32 msg_ctx = 0;
56 gss_buffer_desc desc_buffer;
57 do
58 {
59 local_maj_stat = gss_display_status(
60 &local_min_stat,
61 stat,
62 stat_type,
63 GSS_C_NO_OID,
64 &msg_ctx,
65 &desc_buffer
66 );
67 if (GSS_ERROR(local_maj_stat)) {
68 return GSSAPI_ERROR;
69 }
70
71 if (*desc) {
72 free(*desc);
73 }
74
75 *desc = malloc(desc_buffer.length+1);
76 memcpy(*desc, desc_buffer.value, desc_buffer.length+1);
77
78 gss_release_buffer(&local_min_stat, &desc_buffer);
79 }
80 while(msg_ctx != 0);
81
82 return GSSAPI_OK;
83}
84
85int gssapi_client_init(
86 gssapi_client_state *client,
87 char* spn,
88 char* username,
89 char* password
90)
91{
92 client->cred = GSS_C_NO_CREDENTIAL;
93 client->ctx = GSS_C_NO_CONTEXT;
94
95 client->maj_stat = gssapi_canonicalize_name(&client->min_stat, spn, GSS_C_NT_HOSTBASED_SERVICE, &client->spn);
96 if (GSS_ERROR(client->maj_stat)) {
97 return GSSAPI_ERROR;
98 }
99
100 if (username) {
101 gss_name_t name;
102 client->maj_stat = gssapi_canonicalize_name(&client->min_stat, username, GSS_C_NT_USER_NAME, &name);
103 if (GSS_ERROR(client->maj_stat)) {
104 return GSSAPI_ERROR;
105 }
106
107 if (password) {
108 gss_buffer_desc password_buffer;
109 password_buffer.value = password;
110 password_buffer.length = strlen(password);
111 client->maj_stat = gss_acquire_cred_with_password(&client->min_stat, name, &password_buffer, GSS_C_INDEFINITE, GSS_C_NO_OID_SET, GSS_C_INITIATE, &client->cred, NULL, NULL);
112 } else {
113 client->maj_stat = gss_acquire_cred(&client->min_stat, name, GSS_C_INDEFINITE, GSS_C_NO_OID_SET, GSS_C_INITIATE, &client->cred, NULL, NULL);
114 }
115
116 if (GSS_ERROR(client->maj_stat)) {
117 return GSSAPI_ERROR;
118 }
119
120 OM_uint32 ignored;
121 gss_release_name(&ignored, &name);
122 }
123
124 return GSSAPI_OK;
125}
126
127int gssapi_client_username(
128 gssapi_client_state *client,
129 char** username
130)
131{
132 OM_uint32 ignored;
133 gss_name_t name = GSS_C_NO_NAME;
134
135 client->maj_stat = gss_inquire_context(&client->min_stat, client->ctx, &name, NULL, NULL, NULL, NULL, NULL, NULL);
136 if (GSS_ERROR(client->maj_stat)) {
137 return GSSAPI_ERROR;
138 }
139
140 gss_buffer_desc name_buffer;
141 client->maj_stat = gss_display_name(&client->min_stat, name, &name_buffer, NULL);
142 if (GSS_ERROR(client->maj_stat)) {
143 gss_release_name(&ignored, &name);
144 return GSSAPI_ERROR;
145 }
146
147 *username = malloc(name_buffer.length+1);
148 memcpy(*username, name_buffer.value, name_buffer.length+1);
149
150 gss_release_buffer(&ignored, &name_buffer);
151 gss_release_name(&ignored, &name);
152 return GSSAPI_OK;
153}
154
155int gssapi_client_negotiate(
156 gssapi_client_state *client,
157 void* input,
158 size_t input_length,
159 void** output,
160 size_t* output_length
161)
162{
163 gss_buffer_desc input_buffer = GSS_C_EMPTY_BUFFER;
164 gss_buffer_desc output_buffer = GSS_C_EMPTY_BUFFER;
165
166 if (input) {
167 input_buffer.value = input;
168 input_buffer.length = input_length;
169 }
170
171 client->maj_stat = gss_init_sec_context(
172 &client->min_stat,
173 client->cred,
174 &client->ctx,
175 client->spn,
176 GSS_C_NO_OID,
177 GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG,
178 0,
179 GSS_C_NO_CHANNEL_BINDINGS,
180 &input_buffer,
181 NULL,
182 &output_buffer,
183 NULL,
184 NULL
185 );
186
187 if (output_buffer.length) {
188 *output = malloc(output_buffer.length);
189 *output_length = output_buffer.length;
190 memcpy(*output, output_buffer.value, output_buffer.length);
191
192 OM_uint32 ignored;
193 gss_release_buffer(&ignored, &output_buffer);
194 }
195
196 if (GSS_ERROR(client->maj_stat)) {
197 return GSSAPI_ERROR;
198 } else if (client->maj_stat == GSS_S_CONTINUE_NEEDED) {
199 return GSSAPI_CONTINUE;
200 }
201
202 return GSSAPI_OK;
203}
204
205int gssapi_client_wrap_msg(
206 gssapi_client_state *client,
207 void* input,
208 size_t input_length,
209 void** output,
210 size_t* output_length
211)
212{
213 gss_buffer_desc input_buffer = GSS_C_EMPTY_BUFFER;
214 gss_buffer_desc output_buffer = GSS_C_EMPTY_BUFFER;
215
216 input_buffer.value = input;
217 input_buffer.length = input_length;
218
219 client->maj_stat = gss_wrap(&client->min_stat, client->ctx, 0, GSS_C_QOP_DEFAULT, &input_buffer, NULL, &output_buffer);
220
221 if (output_buffer.length) {
222 *output = malloc(output_buffer.length);
223 *output_length = output_buffer.length;
224 memcpy(*output, output_buffer.value, output_buffer.length);
225
226 gss_release_buffer(&client->min_stat, &output_buffer);
227 }
228
229 if (GSS_ERROR(client->maj_stat)) {
230 return GSSAPI_ERROR;
231 }
232
233 return GSSAPI_OK;
234}
235
236int gssapi_client_destroy(
237 gssapi_client_state *client
238)
239{
240 OM_uint32 ignored;
241 if (client->ctx != GSS_C_NO_CONTEXT) {
242 gss_delete_sec_context(&ignored, &client->ctx, GSS_C_NO_BUFFER);
243 }
244
245 if (client->spn != GSS_C_NO_NAME) {
246 gss_release_name(&ignored, &client->spn);
247 }
248
249 if (client->cred != GSS_C_NO_CREDENTIAL) {
250 gss_release_cred(&ignored, &client->cred);
251 }
252
253 return GSSAPI_OK;
254}
View as plain text