1 | /* |
---|
2 | * Protocol v1, client implementation. |
---|
3 | * |
---|
4 | * This is the client implementation of the old v1 protocol, which doesn't |
---|
5 | * support streaming, keep-alive, or many of the other features of the current |
---|
6 | * protocol. We shoehorn this protocol into the same API as the new protocol |
---|
7 | * so that clients don't have to care, but some functions (like continued |
---|
8 | * commands) will return unimplemented errors. |
---|
9 | * |
---|
10 | * Written by Russ Allbery <rra@stanford.edu> |
---|
11 | * Based on work by Anton Ushakov |
---|
12 | * Copyright 2002, 2003, 2004, 2005, 2006, 2007, 2008 |
---|
13 | * Board of Trustees, Leland Stanford Jr. University |
---|
14 | * |
---|
15 | * See LICENSE for licensing terms. |
---|
16 | */ |
---|
17 | |
---|
18 | #include <config.h> |
---|
19 | #include <portable/system.h> |
---|
20 | #include <portable/gssapi.h> |
---|
21 | #include <portable/socket.h> |
---|
22 | #include <portable/uio.h> |
---|
23 | |
---|
24 | #include <errno.h> |
---|
25 | |
---|
26 | #include <client/internal.h> |
---|
27 | #include <client/remctl.h> |
---|
28 | #include <util/util.h> |
---|
29 | |
---|
30 | |
---|
31 | /* |
---|
32 | * Send a command to the server using protocol v1. Returns true on success, |
---|
33 | * false on failure. |
---|
34 | */ |
---|
35 | bool |
---|
36 | internal_v1_commandv(struct remctl *r, const struct iovec *command, |
---|
37 | size_t count) |
---|
38 | { |
---|
39 | gss_buffer_desc token; |
---|
40 | size_t i; |
---|
41 | char *p; |
---|
42 | OM_uint32 data, major, minor; |
---|
43 | int status; |
---|
44 | |
---|
45 | /* Allocate room for the total message: argc, {<length><arg>}+. */ |
---|
46 | token.length = 4; |
---|
47 | for (i = 0; i < count; i++) |
---|
48 | token.length += 4 + command[i].iov_len; |
---|
49 | token.value = malloc(token.length); |
---|
50 | if (token.value == NULL) { |
---|
51 | internal_set_error(r, "cannot allocate memory: %s", strerror(errno)); |
---|
52 | return false; |
---|
53 | } |
---|
54 | |
---|
55 | /* First, the argument count. Then, each argument. */ |
---|
56 | p = token.value; |
---|
57 | data = htonl(count); |
---|
58 | memcpy(p, &data, 4); |
---|
59 | p += 4; |
---|
60 | for (i = 0; i < count; i++) { |
---|
61 | data = htonl(command[i].iov_len); |
---|
62 | memcpy(p, &data, 4); |
---|
63 | p += 4; |
---|
64 | memcpy(p, command[i].iov_base, command[i].iov_len); |
---|
65 | p += command[i].iov_len; |
---|
66 | } |
---|
67 | |
---|
68 | /* Send the result. */ |
---|
69 | status = token_send_priv(r->fd, r->context, TOKEN_DATA | TOKEN_SEND_MIC, |
---|
70 | &token, &major, &minor); |
---|
71 | if (status != TOKEN_OK) { |
---|
72 | internal_token_error(r, "sending token", status, major, minor); |
---|
73 | free(token.value); |
---|
74 | return false; |
---|
75 | } |
---|
76 | free(token.value); |
---|
77 | r->ready = true; |
---|
78 | return true; |
---|
79 | } |
---|
80 | |
---|
81 | |
---|
82 | /* |
---|
83 | * Retrieve the output from the server using protocol version one and return |
---|
84 | * it. This function will actually be called twice, once to retrieve the |
---|
85 | * output data and once to retrieve the exit status. The old protocol |
---|
86 | * returned those together in one message, so we have to buffer the exit |
---|
87 | * status and return it on the second call. Returns a remctl output struct on |
---|
88 | * success and NULL on failure. |
---|
89 | */ |
---|
90 | struct remctl_output * |
---|
91 | internal_v1_output(struct remctl *r) |
---|
92 | { |
---|
93 | int status, flags; |
---|
94 | gss_buffer_desc token; |
---|
95 | OM_uint32 data, major, minor, length; |
---|
96 | char *p; |
---|
97 | |
---|
98 | /* |
---|
99 | * First, see if we already had an output struct. If so, this is the |
---|
100 | * second call and we should just return the exit status. |
---|
101 | */ |
---|
102 | if (r->output != NULL && !r->ready) { |
---|
103 | if (r->output->type == REMCTL_OUT_STATUS) |
---|
104 | r->output->type = REMCTL_OUT_DONE; |
---|
105 | else { |
---|
106 | internal_output_wipe(r->output); |
---|
107 | r->output->type = REMCTL_OUT_STATUS; |
---|
108 | } |
---|
109 | r->output->status = r->status; |
---|
110 | return r->output; |
---|
111 | } |
---|
112 | |
---|
113 | /* Otherwise, we have to read the token from the server. */ |
---|
114 | status = token_recv_priv(r->fd, r->context, &flags, &token, |
---|
115 | TOKEN_MAX_LENGTH, &major, &minor); |
---|
116 | if (status != TOKEN_OK) { |
---|
117 | internal_token_error(r, "receiving token", status, major, minor); |
---|
118 | if (status == TOKEN_FAIL_EOF) { |
---|
119 | socket_close(r->fd); |
---|
120 | r->fd = -1; |
---|
121 | } |
---|
122 | return NULL; |
---|
123 | } |
---|
124 | if (flags != TOKEN_DATA) { |
---|
125 | internal_set_error(r, "unexpected token from server"); |
---|
126 | gss_release_buffer(&minor, &token); |
---|
127 | return NULL; |
---|
128 | } |
---|
129 | |
---|
130 | /* Extract the return code, message length, and data. */ |
---|
131 | if (token.length < 8) { |
---|
132 | internal_set_error(r, "malformed result token from server"); |
---|
133 | gss_release_buffer(&minor, &token); |
---|
134 | return NULL; |
---|
135 | } |
---|
136 | p = token.value; |
---|
137 | memcpy(&data, p, 4); |
---|
138 | r->status = ntohl(data); |
---|
139 | p += 4; |
---|
140 | memcpy(&data, p, 4); |
---|
141 | length = ntohl(data); |
---|
142 | p += 4; |
---|
143 | if (length != token.length - 8) { |
---|
144 | internal_set_error(r, "malformed result token from server"); |
---|
145 | gss_release_buffer(&minor, &token); |
---|
146 | return NULL; |
---|
147 | } |
---|
148 | |
---|
149 | /* |
---|
150 | * Allocate the new output token. We make another copy of the data, |
---|
151 | * unfortunately, so that we don't have to keep the token around to free |
---|
152 | * later. |
---|
153 | */ |
---|
154 | r->output = malloc(sizeof(struct remctl_output)); |
---|
155 | if (r->output == NULL) { |
---|
156 | internal_set_error(r, "cannot allocate memory: %s", strerror(errno)); |
---|
157 | gss_release_buffer(&minor, &token); |
---|
158 | return NULL; |
---|
159 | } |
---|
160 | r->output->type = REMCTL_OUT_OUTPUT; |
---|
161 | r->output->data = malloc(length); |
---|
162 | if (r->output->data == NULL) { |
---|
163 | internal_set_error(r, "cannot allocate memory: %s", strerror(errno)); |
---|
164 | gss_release_buffer(&minor, &token); |
---|
165 | return NULL; |
---|
166 | } |
---|
167 | memcpy(r->output->data, p, length); |
---|
168 | r->output->length = length; |
---|
169 | gss_release_buffer(&minor, &token); |
---|
170 | |
---|
171 | /* |
---|
172 | * We always claim everything was stdout since we have no way of knowing |
---|
173 | * better with protocol version one. |
---|
174 | */ |
---|
175 | r->output->stream = 1; |
---|
176 | |
---|
177 | /* |
---|
178 | * We can only do one round with protocol version one, so close the |
---|
179 | * connection now. |
---|
180 | */ |
---|
181 | socket_close(r->fd); |
---|
182 | r->fd = -1; |
---|
183 | r->ready = false; |
---|
184 | return r->output; |
---|
185 | } |
---|