1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
|
/* cap-move.c - Moving a capability reference from one task to another.
Copyright (C) 2003 Free Software Foundation, Inc.
Written by Marcus Brinkmann <marcus@gnu.org>
This file is part of the GNU Hurd.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this program; if not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA. */
#if HAVE_CONFIG_H
#include <config.h>
#endif
/* FIXME: This is only some pseudo code to get the hang of it. */
/* Sender side. */
/* Send the capability CAP to DEST. */
error_t
cap_send (hurd_cap_t cap, hurd_cap_t dest, int copy)
{
error_t err;
hurd_cap_scid_t cont_id;
/* This is a low-level RPC to create a new reference container. */
err = hurd_cap_server_create_ref_cont
(cap,
hurd_task_id_from_thread_id (hurd_cap_get_server_thread (dest)),
&cont_id);
if (err)
return err;
/* This is the actual RPC sending the reference in a message to DEST. */
err = hurd_SOME_RPC (dest, ...,
hurd_cap_get_server_thread (cap), cont_id,
...);
if (err)
{
/* FIXME: If this fails, then we can only ignore it. */
hurd_cap_server_destroy_ref_cont (cap, cont_id);
return err;
}
/* We have to deallocate the reference container under all
circumstances. In general, we could trust the server to do it
automatically when the reference container got accepted, and the
receiver of the capability indicates success. However, if there
were a failure we would not know if the reference was received
already, and as the container ID could be reused once it is
released, we would have no way to find out. So this is a
robustness issue, and not a security issue, that forces us to
keep the container ID alive in the server and destroy it here
unconditionally. */
err = hurd_cap_server_destroy_ref_cont (cap, cont_id);
if (err)
return err;
/* If we are moving the capability, we can deallocate it now. If we
are copying the capability, we should of course not do that. */
if (!copy)
{
err = hurd_cap_mod_refs (cap, -1);
if (err)
return err;
}
return 0;
}
/* Receiver side. */
/* Capabilities are received in normal message RPCs. This is the
server stub of one of them.
FIXME: For now this assumes that server_thread is not implemented
by this task. */
error_t
cap_receive (l4_thread_id_t sender_thread,
void *some_object, ..., l4_thread_id_t server_thread,
hurd_cap_scid_t cont_id, ...)
{
error_t err;
hurd_cap_sconn_t sconn;
hurd_cap_t server_task_info = HURD_CAP_NULL;
hurd_task_id_t sender_task = hurd_task_id_from_thread_id (sender_thread);
hurd_cap_scid_t obj_id;
/* We have a chance to inspect the thread ID now and decide if we
want to accept the handle. */
#if FOR_EXAMPLE_A_REAUTHENTICATION_REQUEST
if (server_thread != hurd_cap_get_server_thread (hurd_my_auth ()))
return EINVAL;
#endif
/* Acquire a reference to the server connection if it exists. */
sconn = _hurd_cap_sconn_find (server_thread);
if (sconn)
pthread_mutex_unlock (&sconn->lock);
else
{
/* If no connection to this server exists already, prepare to
create a new one. The sender task ID tells the task server
that we only want to get the info cap if the sender task
still lives at the time it is created (because we rely on the
sender to secure the server task ID). */
/* FIXME: This probably should check for the server being the
task server. However, the implementation could always
guarantee that the task server has an SCONN object
already, so the lookup above would be successful. */
err = hurd_task_info_create (hurd_task_self (),
hurd_task_id_from_thread_id (server_thread),
sender_task,
&server_task_info);
if (err)
return err;
}
/* This is a very low-level RPC to accept the reference from the
server. */
err = hurd_cap_server_accept_ref_cont (server_thread, sender_task,
cont_id, &obj_id);
if (err)
{
if (sconn)
{
pthread_mutex_lock (&sconn->lock);
_hurd_cap_sconn_dealloc (sconn);
}
else
hurd_cap_mod_refs (server_task_info, -1);
return err;
}
/* This consumes our references to sconn and server_task_info, if
any. */
return _hurd_cap_sconn_enter (sconn, server_task_info,
server_thread, obj_id);
}
/* The server side. */
/* Containers are created in the sender's user list, but hold a
reference for the receiver user list. The receiver user list also
has a section with backreferences to all containers for this
receiver.
At look up time, the receiver user list can be looked up. Then the
small list can be searched to verify the request. If it is
verified, the entry can be removed. Then the sender can be looked
up to access the real reference container item. The reference for
the receiver user list should not be released because it will
usually be consumed for the capability.
Without the small list on the receiver user list side there could
be DoS attacks where a malicious receiver constantly claims it
wants to accept a container from another client and keeps the user
list of that client locked during the verification attempts.
First the sender container is created.
Then the receiver container is created.
Removal of containers works the other way round. This is the most
robust way to do it, in case the sender destroys the container
asynchronously with the receiver trying to accept the container.
First an invalid send container (id) has to be allocated, then the
receiver container has to be created, and then the sender container
can be filled with the right receiver container id. */
/* Create a reference container for DEST. */
error_t
hurd_cap_server_create_ref_cont_S (l4_thread_id_t sender_thread,
void *object,
hurd_task_id_t dest,
hurd_cap_scid_t *cont_id)
{
error_t err;
/* FIXME: Needs to be written in more detail. Here is a list:
1. Check if a user list for dest exists, if not, create one. Use
the senders task ID as a constraint to the task_info_create call, to
ensure we don't create a task info cap if the sender dies in the
meantime.
Note: Order matters in the following two steps:
2. Register the container as a sending container with the sender user list.
3. Register the container as a receiving container with the dest
user list. The user dest list should have its own small list just
with containers, and this list should have its own lock. There is
no precondition for this lock. A non-empty list implies one
reference to the task info cap (no explicit reference is taken to
avoid locking problems).
This is how task death notifications should be handled:
If the sender dies, remove the container on both sides, first on the
receiver side, then on the sender side.
If the receiver dies, remove the container on the receiving side,
but keep the container on the sender side. Invalidate the container
on the sender side, so it can not be used anymore (only
destroyed). */
}
/* Accept a reference. */
/* This is a very low-level RPC to accept the reference from the
server. */
error_t
hurd_cap_server_accept_ref_cont (l4_thread_id_t sender_thread,
hurd_task_id_t source,
hurd_cap_scid_t cont_id,
hurd_cap_scid_t *obj_id)
{
/* FIXME: Write this one. This is what should be done:
1. Look up the sender user list. In that, lookup the container with
the given cont id. Check that this containter comes from the task
SOURCE. (This information should be stored in the sender user list!
so no lookup of the SOURCE user list is required up to this point.
2. Now that the validity of the request is confirmed, remove the
container ID from the receiver side. Unlock the sender user list.
3. Look up the SOURCE user list. Look up the container. Look up
the capability that is wrapped by the container. Increase its
reference. Invalidate the container, but keep it around. Unlock
the SOURCE user list.
4. Enter the capability into the SENDER user list. Return its
ID. */
}
error_t
hurd_cap_server_destroy_ref_cont (hurd_cap_t cap, hurd_cap_scid_t cont_id)
{
/* To be written */
}
|