source: web/old/remctl-2.14/server/generic.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: 10.3 KB
Line 
1/*
2 * Server implementation of generic protocol functions.
3 *
4 * These are the server protocol functions that can be shared between the v1
5 * and v2 protocol.
6 *
7 * Written by Russ Allbery <rra@stanford.edu>
8 * Based on work by Anton Ushakov
9 * Copyright 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
10 *     Board of Trustees, Leland Stanford Jr. University
11 *
12 * See LICENSE for licensing terms.
13 */
14
15#include <config.h>
16#include <portable/system.h>
17#include <portable/gssapi.h>
18#include <portable/socket.h>
19#include <portable/uio.h>
20
21#include <server/internal.h>
22#include <util/util.h>
23
24
25/*
26 * Create a new client struct from a file descriptor and establish a GSS-API
27 * context as a specified service with an incoming client and fills out the
28 * client struct.  Returns a new client struct on success and NULL on failure,
29 * logging an appropriate error message.
30 */
31struct client *
32server_new_client(int fd, gss_cred_id_t creds)
33{
34    struct client *client;
35    struct sockaddr_storage ss;
36    socklen_t socklen;
37    size_t length;
38    char *buffer;
39    gss_buffer_desc send_tok, recv_tok, name_buf;
40    gss_name_t name = GSS_C_NO_NAME;
41    gss_OID doid;
42    OM_uint32 major = 0;
43    OM_uint32 minor = 0;
44    OM_uint32 acc_minor;
45    int flags, status;
46    static const OM_uint32 req_gss_flags
47        = (GSS_C_MUTUAL_FLAG | GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG);
48
49    /* Create and initialize a new client struct. */
50    client = xcalloc(1, sizeof(struct client));
51    client->fd = fd;
52    client->context = GSS_C_NO_CONTEXT;
53    client->user = NULL;
54    client->output = NULL;
55    client->hostname = NULL;
56    client->ipaddress = NULL;
57
58    /* Fill in hostname and IP address. */
59    socklen = sizeof(ss);
60    if (getpeername(fd, (struct sockaddr *) &ss, &socklen) != 0) {
61        syswarn("cannot get peer address");
62        goto fail;
63    }
64    length = INET6_ADDRSTRLEN;
65    buffer = xmalloc(length);
66    client->ipaddress = buffer;
67    status = getnameinfo((struct sockaddr *) &ss, socklen, buffer, length,
68                         NULL, 0, NI_NUMERICHOST);
69    if (status != 0) {
70        syswarn("cannot translate IP address of client: %s",
71                gai_strerror(status));
72        goto fail;
73    }
74    length = NI_MAXHOST;
75    buffer = xmalloc(length);
76    status = getnameinfo((struct sockaddr *) &ss, socklen, buffer, length,
77                         NULL, 0, NI_NAMEREQD);
78    if (status == 0)
79        client->hostname = buffer;
80    else
81        free(buffer);
82
83    /* Accept the initial (worthless) token. */
84    status = token_recv(client->fd, &flags, &recv_tok, TOKEN_MAX_LENGTH);
85    if (status != TOKEN_OK) {
86        warn_token("receiving initial token", status, major, minor);
87        goto fail;
88    }
89    free(recv_tok.value);
90    if (flags == (TOKEN_NOOP | TOKEN_CONTEXT_NEXT | TOKEN_PROTOCOL))
91        client->protocol = 2;
92    else if (flags == (TOKEN_NOOP | TOKEN_CONTEXT_NEXT))
93        client->protocol = 1;
94    else {
95        warn("bad token flags %d in initial token", flags);
96        goto fail;
97    }
98
99    /* Now, do the real work of negotiating the context. */
100    do {
101        status = token_recv(client->fd, &flags, &recv_tok, TOKEN_MAX_LENGTH);
102        if (status != TOKEN_OK) {
103            warn_token("receiving context token", status, major, minor);
104            goto fail;
105        }
106        if (flags == TOKEN_CONTEXT)
107            client->protocol = 1;
108        else if (flags != (TOKEN_CONTEXT | TOKEN_PROTOCOL)) {
109            warn("bad token flags %d in context token", flags);
110            free(recv_tok.value);
111            goto fail;
112        }
113        debug("received context token (size=%lu)",
114              (unsigned long) recv_tok.length);
115        major = gss_accept_sec_context(&acc_minor, &client->context, creds,
116                    &recv_tok, GSS_C_NO_CHANNEL_BINDINGS, &name, &doid,
117                    &send_tok, &client->flags, NULL, NULL);
118        free(recv_tok.value);
119
120        /* Send back a token if we need to. */
121        if (send_tok.length != 0) {
122            debug("sending context token (size=%lu)",
123                  (unsigned long) send_tok.length);
124            flags = TOKEN_CONTEXT;
125            if (client->protocol > 1)
126                flags |= TOKEN_PROTOCOL;
127            status = token_send(client->fd, flags, &send_tok);
128            if (status != TOKEN_OK) {
129                warn_token("sending context token", status, major, minor);
130                gss_release_buffer(&minor, &send_tok);
131                goto fail;
132            }
133            gss_release_buffer(&minor, &send_tok);
134        }
135
136        /* Bail out if we lose. */
137        if (major != GSS_S_COMPLETE && major != GSS_S_CONTINUE_NEEDED) {
138            warn_gssapi("while accepting context", major, acc_minor);
139            goto fail;
140        }
141        if (major == GSS_S_CONTINUE_NEEDED)
142            debug("continue needed while accepting context");
143    } while (major == GSS_S_CONTINUE_NEEDED);
144
145    /* Make sure that the appropriate context flags are set. */
146    if (client->protocol > 1) {
147        if ((client->flags & req_gss_flags) != req_gss_flags) {
148            warn("client did not negotiate appropriate GSS-API flags");
149            goto fail;
150        }
151    }
152
153    /* Get the display version of the client name and store it. */
154    major = gss_display_name(&minor, name, &name_buf, &doid);
155    if (major != GSS_S_COMPLETE) {
156        warn_gssapi("while displaying client name", major, minor);
157        goto fail;
158    }
159    major = gss_release_name(&minor, &name);
160    client->user = xstrndup(name_buf.value, name_buf.length);
161    gss_release_buffer(&minor, &name_buf);
162    return client;
163
164fail:
165    if (client->context != GSS_C_NO_CONTEXT)
166        gss_delete_sec_context(&minor, &client->context, GSS_C_NO_BUFFER);
167    if (name != GSS_C_NO_NAME)
168        gss_release_name(&minor, &name);
169    if (client->ipaddress != NULL)
170        free(client->ipaddress);
171    if (client->hostname != NULL)
172        free(client->hostname);
173    free(client);
174    return NULL;
175}
176
177
178/*
179 * Free a client struct, including any resources that it holds.
180 */
181void
182server_free_client(struct client *client)
183{
184    OM_uint32 major, minor;
185
186    if (client->context != GSS_C_NO_CONTEXT) {
187        major = gss_delete_sec_context(&minor, &client->context, NULL);
188        if (major != GSS_S_COMPLETE)
189            warn_gssapi("while deleting context", major, minor);
190    }
191    if (client->output != NULL)
192        free(client->output);
193    if (client->user != NULL)
194        free(client->user);
195    if (client->fd >= 0)
196        close(client->fd);
197    if (client->hostname != NULL)
198        free(client->hostname);
199    if (client->ipaddress != NULL)
200        free(client->ipaddress);
201    free(client);
202}
203
204
205/*
206 * Receives a command token payload and builds an argv structure for it,
207 * returning that as NULL-terminated array of pointers to struct iovecs.
208 * Takes the client struct, a pointer to the beginning of the payload
209 * (starting with the argument count), and the length of the payload.  If
210 * there are any problems with the request, sends an error token, logs the
211 * error, and then returns NULL.  Otherwise, returns the struct iovec array.
212 */
213struct iovec **
214server_parse_command(struct client *client, const char *buffer, size_t length)
215{
216    OM_uint32 tmp;
217    size_t argc, arglen, count;
218    struct iovec **argv = NULL;
219    const char *p = buffer;
220
221    /* Read the argument count. */
222    memcpy(&tmp, p, 4);
223    argc = ntohl(tmp);
224    p += 4;
225    debug("argc is %lu", (unsigned long) argc);
226    if (argc == 0) {
227        warn("command with no arguments");
228        server_send_error(client, ERROR_UNKNOWN_COMMAND, "Unknown command");
229        return NULL;
230    }
231    if (argc > MAXCMDARGS) {
232        warn("too large argc %lu in request message", (unsigned long) argc);
233        server_send_error(client, ERROR_TOOMANY_ARGS, "Too many arguments");
234        return NULL;
235    }
236    if (length - (p - buffer) < 4 * argc) {
237        warn("command data too short");
238        server_send_error(client, ERROR_BAD_COMMAND, "Invalid command token");
239        return NULL;
240    }
241    argv = xcalloc(argc + 1, sizeof(struct iovec *));
242
243    /*
244     * Parse out the arguments and store them into a vector.  Arguments are
245     * packed: (<arglength><argument>)+.  Make sure each time through the loop
246     * that they didn't send more arguments than they claimed to have.
247     */
248    count = 0;
249    while (p <= buffer + length - 4) {
250        if (count >= argc) {
251            warn("sent more arguments than argc %lu", (unsigned long) argc);
252            server_send_error(client, ERROR_BAD_COMMAND,
253                              "Invalid command token");
254            goto fail;
255        }
256        memcpy(&tmp, p, 4);
257        arglen = ntohl(tmp);
258        p += 4;
259        if ((length - (p - buffer)) < arglen) {
260            warn("command data invalid");
261            server_send_error(client, ERROR_BAD_COMMAND,
262                              "Invalid command token");
263            goto fail;
264        }
265        argv[count] = xmalloc(sizeof(struct iovec));
266        argv[count]->iov_len = arglen;
267        if (arglen == 0)
268            argv[count]->iov_base = NULL;
269        else {
270            argv[count]->iov_base = xmalloc(arglen);
271            memcpy(argv[count]->iov_base, p, arglen);
272        }
273        count++;
274        p += arglen;
275        debug("arg %lu has length %lu", (unsigned long) count,
276              (unsigned long) arglen);
277    }
278    if (count != argc || p != buffer + length) {
279        warn("argument count differs from arguments seen");
280        server_send_error(client, ERROR_BAD_COMMAND, "Invalid command token");
281        goto fail;
282    }
283    argv[count] = NULL;
284    return argv;
285
286fail:
287    if (argv != NULL)
288        server_free_command(argv);
289    return NULL;
290}
291
292
293/*
294 * Send an error back to the client.  Takes the client struct, the error code,
295 * and the message to send and dispatches to the appropriate protocol-specific
296 * function.  Returns true on success, false on failure.
297 */
298bool
299server_send_error(struct client *client, enum error_codes error,
300                  const char *message)
301{
302    if (client->protocol > 1)
303        return server_v2_send_error(client, error, message);
304    else {
305        if (client->output != NULL)
306            free(client->output);
307        client->output = xmalloc(strlen(message) + 1);
308        memcpy(client->output, message, strlen(message));
309        client->output[strlen(message)] = '\n';
310        client->outlen = strlen(message) + 1;
311        return server_v1_send_output(client, -1);
312    }
313}
Note: See TracBrowser for help on using the repository browser.