2
* Copyright 1994 by OpenVision Technologies, Inc.
4
* Permission to use, copy, modify, distribute, and sell this software
5
* and its documentation for any purpose is hereby granted without fee,
6
* provided that the above copyright notice appears in all copies and
7
* that both that copyright notice and this permission notice appear in
8
* supporting documentation, and that the name of OpenVision not be used
9
* in advertising or publicity pertaining to distribution of the software
10
* without specific, written prior permission. OpenVision makes no
11
* representations about the suitability of this software for any
12
* purpose. It is provided "as is" without express or implied warranty.
14
* OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16
* EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
18
* USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
19
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
20
* PERFORMANCE OF THIS SOFTWARE.
23
* Copyright (C) 2004 by the Massachusetts Institute of Technology.
24
* All rights reserved.
26
* Export of this software from the United States of America may
27
* require a specific license from the United States Government.
28
* It is the responsibility of any person or organization contemplating
29
* export to obtain such a license before exporting.
31
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
32
* distribute this software and its documentation for any purpose and
33
* without fee is hereby granted, provided that the above copyright
34
* notice appear in all copies and that both that copyright notice and
35
* this permission notice appear in supporting documentation, and that
36
* the name of M.I.T. not be used in advertising or publicity pertaining
37
* to distribution of the software without specific, written prior
38
* permission. Furthermore if you modify this software you must label
39
* your software as modified software and not distribute it in such a
40
* fashion that it might be confused with the original M.I.T. software.
41
* M.I.T. makes no representations about the suitability of
42
* this software for any purpose. It is provided "as is" without express
43
* or implied warranty.
52
#include <gssapi/gssapi_generic.h>
53
#include <gssapi\gssapi_krb5.h>
57
static int verbose = 1;
60
* Function: connect_to_server
62
* Purpose: Opens a TCP connection to the name host and port.
66
* host (r) the target host name
67
* port (r) the target port, in host byte order
69
* Returns: the established socket file desciptor, or -1 on failure
73
* The host name is resolved with gethostbyname(), and the socket is
74
* opened and connected. If an error occurs, an error message is
75
* displayed and -1 is returned.
77
static int connect_to_server(host, port)
81
struct sockaddr_in saddr;
85
if ((hp = gethostbyname(host)) == NULL) {
86
printf("Unknown host: %s\r\n", host);
90
saddr.sin_family = hp->h_addrtype;
91
memcpy((char *)&saddr.sin_addr, hp->h_addr, sizeof(saddr.sin_addr));
92
saddr.sin_port = htons(port);
94
if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
95
perror("creating socket");
98
if (connect(s, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) {
99
perror("connecting to server");
100
(void) closesocket(s);
107
* Function: client_establish_context
109
* Purpose: establishes a GSS-API context with a specified service and
110
* returns the context handle
114
* s (r) an established TCP connection to the service
115
* service_name (r) the ASCII service name of the service
116
* gss_flags (r) GSS-API delegation flag (if any)
117
* auth_flag (r) whether to actually do authentication
118
* oid (r) OID of the mechanism to use
119
* context (w) the established GSS-API context
120
* ret_flags (w) the returned flags from init_sec_context
122
* Returns: 0 on success, -1 on failure
126
* service_name is imported as a GSS-API name and a GSS-API context is
127
* established with the corresponding service; the service should be
128
* listening on the TCP connection s. The default GSS-API mechanism
129
* is used, and mutual authentication and replay detection are
132
* If successful, the context handle is returned in context. If
133
* unsuccessful, the GSS-API error messages are displayed on stderr
134
* and -1 is returned.
136
int client_establish_context( int s,
142
gss_ctx_id_t *gss_context,
143
OM_uint32 *ret_flags)
146
gss_buffer_desc send_tok, recv_tok, *token_ptr;
147
gss_name_t target_name;
148
OM_uint32 maj_stat, min_stat, init_sec_min_stat;
152
* Import the name into target_name. Use send_tok to save
153
* local variable space.
155
send_tok.value = service_name;
156
send_tok.length = strlen(service_name) ;
157
maj_stat = gss_import_name(&min_stat, &send_tok,
158
(gss_OID) gss_nt_service_name, &target_name);
159
if (maj_stat != GSS_S_COMPLETE) {
160
display_status("parsing name", maj_stat, min_stat);
165
if (send_token(s, TOKEN_NOOP|TOKEN_CONTEXT_NEXT, empty_token) < 0) {
166
(void) gss_release_name(&min_stat, &target_name);
172
* Perform the context-establishement loop.
174
* On each pass through the loop, token_ptr points to the token
175
* to send to the server (or GSS_C_NO_BUFFER on the first pass).
176
* Every generated token is stored in send_tok which is then
177
* transmitted to the server; every received token is stored in
178
* recv_tok, which token_ptr is then set to, to be processed by
179
* the next call to gss_init_sec_context.
181
* GSS-API guarantees that send_tok's length will be non-zero
182
* if and only if the server is expecting another token from us,
183
* and that gss_init_sec_context returns GSS_S_CONTINUE_NEEDED if
184
* and only if the server has another token to send us.
187
token_ptr = GSS_C_NO_BUFFER;
188
*gss_context = GSS_C_NO_CONTEXT;
192
gss_init_sec_context(&init_sec_min_stat,
199
NULL, /* no channel bindings */
201
NULL, /* ignore mech type */
204
NULL); /* ignore time_rec */
206
if (token_ptr != GSS_C_NO_BUFFER)
207
free (recv_tok.value);
209
if (send_tok.length != 0) {
211
printf("Sending init_sec_context token (size=%d)...",
212
(int) send_tok.length);
213
if (send_token(s, v1_format?0:TOKEN_CONTEXT, &send_tok) < 0) {
214
(void) gss_release_buffer(&min_stat, &send_tok);
215
(void) gss_release_name(&min_stat, &target_name);
219
(void) gss_release_buffer(&min_stat, &send_tok);
221
if (maj_stat!=GSS_S_COMPLETE && maj_stat!=GSS_S_CONTINUE_NEEDED) {
222
display_status("initializing context", maj_stat,
224
(void) gss_release_name(&min_stat, &target_name);
225
if (*gss_context != GSS_C_NO_CONTEXT)
226
gss_delete_sec_context(&min_stat, gss_context,
231
if (maj_stat == GSS_S_CONTINUE_NEEDED) {
233
printf("continue needed...");
234
if (recv_token(s, &token_flags, &recv_tok) < 0) {
235
(void) gss_release_name(&min_stat, &target_name);
238
token_ptr = &recv_tok;
242
} while (maj_stat == GSS_S_CONTINUE_NEEDED);
244
(void) gss_release_name(&min_stat, &target_name);
247
if (send_token(s, TOKEN_NOOP, empty_token) < 0)
254
static void read_file(file_name, in_buf)
259
struct stat stat_buf;
261
if ((fd = open(file_name, O_RDONLY, 0)) < 0) {
263
printf("Couldn't open file %s\r\n", file_name);
266
if (fstat(fd, &stat_buf) < 0) {
270
in_buf->length = stat_buf.st_size;
272
if (in_buf->length == 0) {
273
in_buf->value = NULL;
277
if ((in_buf->value = malloc(in_buf->length)) == 0) {
278
printf("Couldn't allocate %d byte buffer for reading file\r\n",
279
(int) in_buf->length);
283
/* this code used to check for incomplete reads, but you can't get
284
an incomplete read on any file for which fstat() is meaningful */
286
count = read(fd, in_buf->value, in_buf->length);
291
if (count < in_buf->length)
292
printf("Warning, only read in %d bytes, expected %d\r\n",
293
count, (int) in_buf->length);
297
* Function: call_server
299
* Purpose: Call the "sign" service.
303
* host (r) the host providing the service
304
* port (r) the port to connect to on host
305
* service_name (r) the GSS-API service name to authenticate to
306
* gss_flags (r) GSS-API delegation flag (if any)
307
* auth_flag (r) whether to do authentication
308
* wrap_flag (r) whether to do message wrapping at all
309
* encrypt_flag (r) whether to do encryption while wrapping
310
* mic_flag (r) whether to request a MIC from the server
311
* msg (r) the message to have "signed"
312
* use_file (r) whether to treat msg as an input file name
313
* mcount (r) the number of times to send the message
315
* Returns: 0 on success, -1 on failure
319
* call_server opens a TCP connection to <host:port> and establishes a
320
* GSS-API context with service_name over the connection. It then
321
* seals msg in a GSS-API token with gss_wrap, sends it to the server,
322
* reads back a GSS-API signature block for msg from the server, and
323
* verifies it with gss_verify. -1 is returned if any step fails,
324
* otherwise 0 is returned. */
325
int call_server(char *host, u_short port, gss_OID oid, char *service_name,
326
OM_uint32 gss_flags, int auth_flag,
327
int wrap_flag, int encrypt_flag, int mic_flag, int v1_format,
328
char *msg, int use_file, int mcount)
330
gss_ctx_id_t context;
331
gss_buffer_desc in_buf, out_buf;
334
OM_uint32 maj_stat, min_stat;
335
gss_name_t src_name, targ_name;
336
gss_buffer_desc sname, tname;
338
gss_OID mechanism, name_type;
340
OM_uint32 context_flags;
343
gss_OID_set mech_names;
344
gss_buffer_desc oid_name;
348
/* Open connection */
349
if ((s = connect_to_server(host, port)) < 0)
352
/* Establish context */
353
if (client_establish_context(s, service_name, gss_flags, auth_flag,
354
v1_format, oid, &context,
356
(void) closesocket(s);
362
/* display the flags */
363
/* display_ctx_flags(ret_flags); */
365
/* Get context information */
366
maj_stat = gss_inquire_context(&min_stat, context,
367
&src_name, &targ_name, &lifetime,
368
&mechanism, &context_flags,
371
if (maj_stat != GSS_S_COMPLETE) {
372
display_status("inquiring context", maj_stat, min_stat);
376
maj_stat = gss_display_name(&min_stat, src_name, &sname,
378
if (maj_stat != GSS_S_COMPLETE) {
379
display_status("displaying source name", maj_stat, min_stat);
382
maj_stat = gss_display_name(&min_stat, targ_name, &tname,
384
if (maj_stat != GSS_S_COMPLETE) {
385
display_status("displaying target name", maj_stat, min_stat);
388
printf("\"%.*s\" to \"%.*s\", lifetime %d, flags %x, %s, %s\r\n",
389
(int) sname.length, (char *) sname.value,
390
(int) tname.length, (char *) tname.value, lifetime,
392
(is_local) ? "locally initiated" : "remotely initiated",
393
(is_open) ? "open" : "closed");
395
(void) gss_release_name(&min_stat, &src_name);
396
(void) gss_release_name(&min_stat, &targ_name);
397
(void) gss_release_buffer(&min_stat, &sname);
398
(void) gss_release_buffer(&min_stat, &tname);
400
maj_stat = gss_oid_to_str(&min_stat,
403
if (maj_stat != GSS_S_COMPLETE) {
404
display_status("converting oid->string", maj_stat, min_stat);
407
printf("Name type of source name is %.*s.\r\n",
408
(int) oid_name.length, (char *) oid_name.value);
409
(void) gss_release_buffer(&min_stat, &oid_name);
411
/* Now get the names supported by the mechanism */
412
maj_stat = gss_inquire_names_for_mech(&min_stat,
415
if (maj_stat != GSS_S_COMPLETE) {
416
display_status("inquiring mech names", maj_stat, min_stat);
420
maj_stat = gss_oid_to_str(&min_stat,
423
if (maj_stat != GSS_S_COMPLETE) {
424
display_status("converting oid->string", maj_stat, min_stat);
427
printf("Mechanism %.*s supports %d names\r\n",
428
(int) oid_name.length, (char *) oid_name.value,
429
(int) mech_names->count);
430
(void) gss_release_buffer(&min_stat, &oid_name);
432
for (i=0; i<mech_names->count; i++) {
433
maj_stat = gss_oid_to_str(&min_stat,
434
&mech_names->elements[i],
436
if (maj_stat != GSS_S_COMPLETE) {
437
display_status("converting oid->string", maj_stat, min_stat);
440
printf(" %d: %.*s\r\n", (int) i,
441
(int) oid_name.length, (char *) oid_name.value);
443
(void) gss_release_buffer(&min_stat, &oid_name);
445
(void) gss_release_oid_set(&min_stat, &mech_names);
450
read_file(msg, &in_buf);
452
/* Seal the message */
454
in_buf.length = strlen(msg);
457
for (i = 0; i < mcount; i++) {
459
maj_stat = gss_wrap(&min_stat, context, encrypt_flag, GSS_C_QOP_DEFAULT,
460
&in_buf, &state, &out_buf);
461
if (maj_stat != GSS_S_COMPLETE) {
462
display_status("wrapping message", maj_stat, min_stat);
463
(void) closesocket(s);
464
(void) gss_delete_sec_context(&min_stat, &context, GSS_C_NO_BUFFER);
466
} else if (encrypt_flag && ! state) {
467
fprintf(stderr, "Warning! Message not encrypted.\r\n");
475
if (send_token(s, (v1_format?0
477
(wrap_flag ? TOKEN_WRAPPED : 0) |
478
(encrypt_flag ? TOKEN_ENCRYPTED : 0) |
479
(mic_flag ? TOKEN_SEND_MIC : 0))), &out_buf) < 0) {
480
(void) closesocket(s);
481
(void) gss_delete_sec_context(&min_stat, &context, GSS_C_NO_BUFFER);
484
if (out_buf.value != in_buf.value)
485
(void) gss_release_buffer(&min_stat, &out_buf);
487
/* Read signature block into out_buf */
488
if (recv_token(s, &token_flags, &out_buf) < 0) {
489
(void) closesocket(s);
490
(void) gss_delete_sec_context(&min_stat, &context, GSS_C_NO_BUFFER);
495
/* Verify signature block */
496
maj_stat = gss_verify_mic(&min_stat, context, &in_buf,
497
&out_buf, &qop_state);
498
if (maj_stat != GSS_S_COMPLETE) {
499
display_status("verifying signature", maj_stat, min_stat);
500
(void) closesocket(s);
501
(void) gss_delete_sec_context(&min_stat, &context, GSS_C_NO_BUFFER);
506
printf("Signature verified.\r\n");
510
printf("Response received.\r\n");
513
free (out_buf.value);
521
(void) send_token(s, TOKEN_NOOP, empty_token);
525
maj_stat = gss_delete_sec_context(&min_stat, &context, &out_buf);
526
if (maj_stat != GSS_S_COMPLETE) {
527
display_status("deleting context", maj_stat, min_stat);
528
(void) closesocket(s);
529
(void) gss_delete_sec_context(&min_stat, &context, GSS_C_NO_BUFFER);
533
(void) gss_release_buffer(&min_stat, &out_buf);
536
(void) closesocket(s);
540
static void parse_oid(char *mechanism, gss_OID *oid)
542
char *mechstr = 0, *cp;
544
OM_uint32 maj_stat, min_stat;
546
if (isdigit((int) mechanism[0])) {
547
mechstr = malloc(strlen(mechanism)+5);
549
printf("Couldn't allocate mechanism scratch!\r\n");
552
sprintf(mechstr, "{ %s }", mechanism);
553
for (cp = mechstr; *cp; cp++)
558
tok.value = mechanism;
559
tok.length = strlen(tok.value);
560
maj_stat = gss_str_to_oid(&min_stat, &tok, oid);
561
if (maj_stat != GSS_S_COMPLETE) {
562
display_status("str_to_oid", maj_stat, min_stat);
570
gss (char *server_host, char *service_name, char *mechanism, char *msg, int port,
571
int verbose, int delegate, int mutual, int replay, int sequence,
572
int v1_format, int auth_flag, int wrap_flag,
573
int encrypt_flag, int mic_flag, int ccount, int mcount, char *ccache)
576
OM_uint32 gss_flags = 0, min_stat;
577
gss_OID oid = GSS_C_NULL_OID;
578
OM_uint32 minor_status;
582
if (ccount <= 0) ccount = 1;
583
if (mcount <= 0) mcount = 1;
585
if (mechanism && mechanism[0])
586
parse_oid(mechanism, &oid);
589
gss_flags |= GSS_C_DELEG_FLAG;
591
gss_flags |= GSS_C_MUTUAL_FLAG;
593
gss_flags |= GSS_C_REPLAY_FLAG;
595
gss_flags |= GSS_C_SEQUENCE_FLAG;
597
/* By using this function the independence between the application and
598
* the underlying authentication system is broken
600
if ( ccache && ccache[0] )
601
gss_krb5_ccache_name(&minor_status, ccache, NULL);
603
for (i = 0; i < ccount; i++) {
604
if (call_server(server_host, port, oid, service_name,
605
gss_flags, auth_flag, wrap_flag, encrypt_flag, mic_flag,
606
v1_format, msg, use_file, mcount) < 0)
611
if (oid != GSS_C_NULL_OID)
612
(void) gss_release_oid(&min_stat, &oid);