source: web/old/remctl-2.14/client/open.c @ f6f3e91

web
Last change on this file since f6f3e91 was f6f3e91, checked in by Jessica B. Hamrick <jhamrick@…>, 15 years ago

Preserve directory hierarchy (not sure what happened to it)

  • Property mode set to 100644
File size: 9.1 KB
Line 
1/*
2 * Open a connection to a remote server.
3 *
4 * This is the client implementation of opening a connection to a remote
5 * server and doing the initial GSS-API negotiation.  This function is shared
6 * between the v1 and v2 implementations.  One of the things it establishes is
7 * what protocol is being used.
8 *
9 * Written by Russ Allbery <rra@stanford.edu>
10 * Based on work by Anton Ushakov
11 * Copyright 2002, 2003, 2004, 2005, 2006, 2007, 2008
12 *     Board of Trustees, Leland Stanford Jr. University
13 *
14 * See LICENSE for licensing terms.
15 */
16
17#include <config.h>
18#include <portable/system.h>
19#include <portable/gssapi.h>
20#include <portable/socket.h>
21
22#include <errno.h>
23
24#include <client/internal.h>
25#include <client/remctl.h>
26#include <util/util.h>
27
28
29/*
30 * Given the remctl object (for error reporting), host, and port, attempt a
31 * network connection.  Returns the file descriptor if successful or -1 on
32 * failure.
33 */
34static int
35internal_connect(struct remctl *r, const char *host, unsigned short port)
36{
37    struct addrinfo hints, *ai;
38    char portbuf[16];
39    int status, fd;
40
41    /*
42     * Look up the remote host and open a TCP connection.  Call getaddrinfo
43     * and network_connect instead of network_connect_host so that we can
44     * report the complete error on host resolution.
45     */
46    memset(&hints, 0, sizeof(hints));
47    hints.ai_family = AF_UNSPEC;
48    hints.ai_socktype = SOCK_STREAM;
49    snprintf(portbuf, sizeof(portbuf), "%hu", port);
50    status = getaddrinfo(host, portbuf, &hints, &ai);
51    if (status != 0) {
52        internal_set_error(r, "unknown host %s: %s", host,
53                           gai_strerror(status));
54        return -1;
55    }
56    fd = network_connect(ai, NULL);
57    freeaddrinfo(ai);
58    if (fd < 0) {
59        internal_set_error(r, "cannot connect to %s (port %hu): %s", host,
60                           port, socket_strerror(socket_errno));
61        return -1;
62    }
63    return fd;
64}
65
66
67/*
68 * Given the remctl struct, the host to connect to, the principal name (which
69 * may be NULL to use the default), and a pointer to a gss_name_t, import that
70 * principal into a GSS-API name.  We want to use a host-based name if
71 * possible since that will trigger domain to realm mapping and name
72 * canonicalization if desired, but given an arbitrary Kerberos principal, we
73 * don't know whether it's host-based or not.  Therefore, if the principal was
74 * specified explicitly, always just use it.
75 *
76 * Returns true on success and false on failure.
77 */
78static bool
79internal_import_name(struct remctl *r, const char *host,
80                     const char *principal, gss_name_t *name)
81{
82    gss_buffer_desc name_buffer;
83    char *defprinc = NULL;
84    size_t length;
85    OM_uint32 major, minor;
86    gss_OID oid;
87
88    /*
89     * If principal is NULL, use host@<host>.  Don't use concat here since it
90     * dies on failure and that's rude for a library.
91     */
92    if (principal == NULL) {
93        length = strlen("host@") + strlen(host) + 1;
94        defprinc = malloc(length);
95        if (defprinc == NULL) {
96            internal_set_error(r, "cannot allocate memory: %s",
97                               strerror(errno));
98            return false;
99        }
100        strlcpy(defprinc, "host@", length);
101        strlcat(defprinc, host, length);
102        principal = defprinc;
103    }
104
105    /*
106     * Import the name.  If principal was null, we use a host-based OID;
107     * otherwise, specify that the name is a Kerberos principal.
108     */
109    name_buffer.value = (char *) principal;
110    name_buffer.length = strlen(principal) + 1;
111    if (defprinc == NULL)
112        oid = GSS_C_NT_USER_NAME;
113    else
114        oid = GSS_C_NT_HOSTBASED_SERVICE;
115    major = gss_import_name(&minor, &name_buffer, oid, name);
116    if (defprinc != NULL)
117        free(defprinc);
118    if (major != GSS_S_COMPLETE) {
119        internal_gssapi_error(r, "parsing name", major, minor);
120        return false;
121    }
122    return true;
123}
124
125
126/*
127 * Open a new connection to a server.  Returns true on success, false on
128 * failure.  On failure, sets the error message appropriately.
129 */
130bool
131internal_open(struct remctl *r, const char *host, unsigned short port,
132              const char *principal)
133{
134    int status, flags;
135    bool port_fallback = false;
136    int fd = -1;
137    gss_buffer_desc send_tok, recv_tok, *token_ptr;
138    gss_buffer_desc empty_token = { 0, (void *) "" };
139    gss_name_t name = GSS_C_NO_NAME;
140    gss_ctx_id_t gss_context = GSS_C_NO_CONTEXT;
141    OM_uint32 major, minor, init_minor, gss_flags;
142    static const OM_uint32 wanted_gss_flags
143        = (GSS_C_MUTUAL_FLAG | GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG
144           | GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG);
145    static const OM_uint32 req_gss_flags
146        = (GSS_C_MUTUAL_FLAG | GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG);
147
148    /*
149     * If port is 0, default to trying the standard port and then falling back
150     * on the old port.
151     */
152    if (port == 0) {
153        port = REMCTL_PORT;
154        port_fallback = true;
155    }
156
157    /* Make the network connection. */
158    fd = internal_connect(r, host, port);
159    if (fd < 0 && port_fallback)
160        fd = internal_connect(r, host, REMCTL_PORT_OLD);
161    if (fd < 0)
162        goto fail;
163    r->fd = fd;
164
165    /* Import the name. */
166    if (!internal_import_name(r, host, principal, &name))
167        goto fail;
168
169    /*
170     * Default to protocol version two, but if some other protocol is already
171     * set in the remctl struct, don't override.  This facility is used only
172     * for testing currently.
173     */
174    if (r->protocol == 0)
175        r->protocol = 2;
176
177    /* Send the initial negotiation token. */
178    status = token_send(fd, TOKEN_NOOP | TOKEN_CONTEXT_NEXT | TOKEN_PROTOCOL,
179                        &empty_token);
180    if (status != TOKEN_OK) {
181        internal_token_error(r, "sending initial token", status, 0, 0);
182        goto fail;
183    }
184
185    /* Perform the context-establishment loop.
186     *
187     * On each pass through the loop, token_ptr points to the token to send to
188     * the server (or GSS_C_NO_BUFFER on the first pass).  Every generated
189     * token is stored in send_tok which is then transmitted to the server;
190     * every received token is stored in recv_tok, which token_ptr is then set
191     * to, to be processed by the next call to gss_init_sec_context.
192     *
193     * GSS-API guarantees that send_tok's length will be non-zero if and only
194     * if the server is expecting another token from us, and that
195     * gss_init_sec_context returns GSS_S_CONTINUE_NEEDED if and only if the
196     * server has another token to send us.
197     *
198     * We start with the assumption that we're going to do protocol v2, but if
199     * the server ever drops TOKEN_PROTOCOL from the response, we fall back to
200     * v1.
201     */
202    token_ptr = GSS_C_NO_BUFFER;
203    do {
204        major = gss_init_sec_context(&init_minor, GSS_C_NO_CREDENTIAL, 
205                    &gss_context, name, (const gss_OID) GSS_KRB5_MECHANISM,
206                    wanted_gss_flags, 0, NULL, token_ptr, NULL, &send_tok,
207                    &gss_flags, NULL);
208        if (token_ptr != GSS_C_NO_BUFFER)
209            free(recv_tok.value);
210
211        /* If we have anything more to say, send it. */
212        if (send_tok.length != 0) {
213            flags = TOKEN_CONTEXT;
214            if (r->protocol > 1)
215                flags |= TOKEN_PROTOCOL;
216            status = token_send(fd, flags, &send_tok);
217            if (status != TOKEN_OK) {
218                internal_token_error(r, "sending token", status, major, minor);
219                gss_release_buffer(&minor, &send_tok);
220                goto fail;
221            }
222        }
223        gss_release_buffer(&minor, &send_tok);
224
225        /* On error, report the error and abort. */
226        if (major != GSS_S_COMPLETE && major != GSS_S_CONTINUE_NEEDED) {
227            internal_gssapi_error(r, "initializing context", major,
228                                  init_minor);
229            goto fail;
230        }
231
232        /*
233         * If the flags we get back from the server are bad and we're doing
234         * protocol v2, report an error and abort.
235         */
236        if (r->protocol > 1 && (gss_flags & req_gss_flags) != req_gss_flags) {
237            internal_set_error(r, "server did not negotiate acceptable"
238                               " GSS-API flags");
239            goto fail;
240        }
241
242        /* If we're still expecting more, retrieve it. */
243        if (major == GSS_S_CONTINUE_NEEDED) {
244            status = token_recv(fd, &flags, &recv_tok, TOKEN_MAX_LENGTH);
245            if (status != TOKEN_OK) {
246                internal_token_error(r, "receiving token", status, major,
247                                     minor);
248                goto fail;
249            }
250            if (r->protocol > 1 && (flags & TOKEN_PROTOCOL) != TOKEN_PROTOCOL)
251                r->protocol = 1;
252            token_ptr = &recv_tok;
253        }
254    } while (major == GSS_S_CONTINUE_NEEDED);
255
256    r->context = gss_context;
257    r->ready = 0;
258    gss_release_name(&minor, &name);
259    return true;
260
261fail:
262    if (fd >= 0)
263        socket_close(fd);
264    r->fd = -1;
265    if (name != GSS_C_NO_NAME)
266        gss_release_name(&minor, &name);
267    if (gss_context != GSS_C_NO_CONTEXT)
268        gss_delete_sec_context(&minor, &gss_context, GSS_C_NO_BUFFER);
269    return false;
270}
Note: See TracBrowser for help on using the repository browser.