1 | /* |
---|
2 | * Token handling routines. |
---|
3 | * |
---|
4 | * Low-level routines to send and receive remctl tokens. token_send and |
---|
5 | * token_recv do not do anything to their provided input or output except |
---|
6 | * wrapping flags and a length around them. |
---|
7 | * |
---|
8 | * Originally written by Anton Ushakov |
---|
9 | * Extensive modifications by Russ Allbery <rra@stanford.edu> |
---|
10 | * Copyright 2002, 2003, 2004, 2005, 2006, 2007, 2008 |
---|
11 | * Board of Trustees, Leland Stanford Jr. University |
---|
12 | * |
---|
13 | * See LICENSE for licensing terms. |
---|
14 | */ |
---|
15 | |
---|
16 | #include <config.h> |
---|
17 | #include <portable/system.h> |
---|
18 | #include <portable/gssapi.h> |
---|
19 | #include <portable/socket.h> |
---|
20 | |
---|
21 | #include <errno.h> |
---|
22 | |
---|
23 | #include <util/util.h> |
---|
24 | |
---|
25 | /* |
---|
26 | * Windows requires a different function when sending to sockets, but can't |
---|
27 | * return short writes on blocking sockets. |
---|
28 | */ |
---|
29 | #ifdef _WIN32 |
---|
30 | # define socket_xwrite(fd, b, s) send((fd), (b), (s), 0) |
---|
31 | #else |
---|
32 | # define socket_xwrite(fd, b, s) xwrite((fd), (b), (s)) |
---|
33 | #endif |
---|
34 | |
---|
35 | |
---|
36 | /* |
---|
37 | * Equivalent to read, but reads all the available data up to the buffer |
---|
38 | * length, using multiple reads if needed and handling EINTR and EAGAIN. |
---|
39 | */ |
---|
40 | static ssize_t |
---|
41 | xread(int fd, void *buffer, size_t size) |
---|
42 | { |
---|
43 | size_t total; |
---|
44 | ssize_t status; |
---|
45 | int count = 0; |
---|
46 | |
---|
47 | if (size == 0) |
---|
48 | return 0; |
---|
49 | |
---|
50 | /* Abort the read if we try 100 times with no forward progress. */ |
---|
51 | for (total = 0; total < size; total += status) { |
---|
52 | if (++count > 100) |
---|
53 | break; |
---|
54 | status = socket_read(fd, (char *) buffer + total, size - total); |
---|
55 | if (status > 0) |
---|
56 | count = 0; |
---|
57 | if (status < 0) { |
---|
58 | if ((errno != EINTR) && (errno != EAGAIN)) |
---|
59 | break; |
---|
60 | status = 0; |
---|
61 | } |
---|
62 | } |
---|
63 | return (status < 0) ? status : (ssize_t) total; |
---|
64 | } |
---|
65 | |
---|
66 | |
---|
67 | /* |
---|
68 | * Send a token to a file descriptor. Takes the file descriptor, the token, |
---|
69 | * and the flags (a single byte, even though they're passed in as an integer) |
---|
70 | * and writes them to the file descriptor. Returns TOKEN_OK on success and |
---|
71 | * TOKEN_FAIL_SYSTEM or TOKEN_FAIL_SOCKET on an error (including partial |
---|
72 | * writes). |
---|
73 | */ |
---|
74 | enum token_status |
---|
75 | token_send(int fd, int flags, gss_buffer_t tok) |
---|
76 | { |
---|
77 | ssize_t status; |
---|
78 | size_t buflen; |
---|
79 | char *buffer; |
---|
80 | unsigned char char_flags = (unsigned char) flags; |
---|
81 | OM_uint32 len = htonl(tok->length); |
---|
82 | |
---|
83 | /* Send out the whole message in a single write. */ |
---|
84 | buflen = 1 + sizeof(OM_uint32) + tok->length; |
---|
85 | buffer = malloc(buflen); |
---|
86 | if (buffer == NULL) |
---|
87 | return TOKEN_FAIL_SYSTEM; |
---|
88 | memcpy(buffer, &char_flags, 1); |
---|
89 | memcpy(buffer + 1, &len, sizeof(OM_uint32)); |
---|
90 | memcpy(buffer + 1 + sizeof(OM_uint32), tok->value, tok->length); |
---|
91 | status = socket_xwrite(fd, buffer, buflen); |
---|
92 | free(buffer); |
---|
93 | if (status < 0 || (size_t) status != buflen) |
---|
94 | return TOKEN_FAIL_SOCKET; |
---|
95 | else |
---|
96 | return TOKEN_OK; |
---|
97 | } |
---|
98 | |
---|
99 | |
---|
100 | /* |
---|
101 | * Receive a token from a file descriptor. Takes the file descriptor, a |
---|
102 | * buffer into which to store the token, a pointer into which to store the |
---|
103 | * flags, and the maximum token length we're willing to accept. Returns |
---|
104 | * TOKEN_OK on success. On failure, returns one of: |
---|
105 | * |
---|
106 | * TOKEN_FAIL_SYSTEM System call failed, errno set. |
---|
107 | * TOKEN_FAIL_SOCKET Socket call failed, socket_errno set. |
---|
108 | * TOKEN_FAIL_INVALID Invalid token format. |
---|
109 | * TOKEN_FAIL_LARGE Token data larger than provided limit. |
---|
110 | * TOKEN_FAIL_EOF Unexpected end of file |
---|
111 | * |
---|
112 | * TOKEN_FAIL_SYSTEM and TOKEN_FAIL_SOCKET are the same on UNIX but different |
---|
113 | * on Windows. |
---|
114 | * |
---|
115 | * recv_token reads the token flags (a single byte, even though they're stored |
---|
116 | * into an integer, then reads the token length (as a network long), allocates |
---|
117 | * memory to hold the data, and then reads the token data from the file |
---|
118 | * descriptor. It blocks to read the length and data, if necessary. On a |
---|
119 | * successful return, the value member of the token should be freed with |
---|
120 | * free(). |
---|
121 | */ |
---|
122 | enum token_status |
---|
123 | token_recv(int fd, int *flags, gss_buffer_t tok, size_t max) |
---|
124 | { |
---|
125 | ssize_t status; |
---|
126 | OM_uint32 len; |
---|
127 | unsigned char char_flags; |
---|
128 | |
---|
129 | status = xread(fd, &char_flags, 1); |
---|
130 | if (status < 0) |
---|
131 | return TOKEN_FAIL_SOCKET; |
---|
132 | else if (status == 0) |
---|
133 | return TOKEN_FAIL_EOF; |
---|
134 | *flags = char_flags; |
---|
135 | |
---|
136 | status = xread(fd, &len, sizeof(OM_uint32)); |
---|
137 | if (status < 0) |
---|
138 | return TOKEN_FAIL_SOCKET; |
---|
139 | else if (status == 0) |
---|
140 | return TOKEN_FAIL_EOF; |
---|
141 | else if (status != sizeof(OM_uint32)) |
---|
142 | return TOKEN_FAIL_INVALID; |
---|
143 | tok->length = ntohl(len); |
---|
144 | if (tok->length > max) |
---|
145 | return TOKEN_FAIL_LARGE; |
---|
146 | |
---|
147 | tok->value = malloc(tok->length); |
---|
148 | if (tok->value == NULL) |
---|
149 | return TOKEN_FAIL_SYSTEM; |
---|
150 | status = xread(fd, tok->value, tok->length); |
---|
151 | if (status < 0) { |
---|
152 | free(tok->value); |
---|
153 | return TOKEN_FAIL_SOCKET; |
---|
154 | } else if (status != (ssize_t) tok->length) { |
---|
155 | free(tok->value); |
---|
156 | return TOKEN_FAIL_INVALID; |
---|
157 | } |
---|
158 | return TOKEN_OK; |
---|
159 | } |
---|