* [ SEPOL ] Extract user records from binary policy
@ 2005-09-24 5:21 Ivan Gyurdiev
2005-09-24 5:31 ` Ivan Gyurdiev
0 siblings, 1 reply; 7+ messages in thread
From: Ivan Gyurdiev @ 2005-09-24 5:21 UTC (permalink / raw)
To: selinux; +Cc: dwalsh
[-- Attachment #1: Type: text/plain, Size: 1010 bytes --]
The attached patch does the following:
- makes a couple of internal functions static
- un-inlines a boolean function
- changes existing sepol_valid_users_list, which returns tha names of
valid users, to sepol_list_users, which returns an array of records,
more suitable for semanage dbase operations (plus that includes all the
data, not just names). This is fine for a short list users - may not be
the appropriate thing to do for something like allow rules.
I did actually try this, so it should work :)
Here's the old genusers loading the MLS users, and then list_users
extracting the info back in record format:
[phantom@cobra src]$ ./test /etc/selinux/mls/policy/policy.19
User: system_u with default MLS level s2, range s0-s9:c0.c127, roles:
system_r
User: user_u with default MLS level s2, range s2, roles: user_r
User: root with default MLS level s2, range s0-s9:c0.c127, roles:
system_r sysadm_r staff_r secadm_r
./test: Warning! unable to get boolean names: No such file or directory
[-- Attachment #2: libsepol.users.list.diff --]
[-- Type: text/x-patch, Size: 5353 bytes --]
diff -Nrua libsepol.new/include/sepol/users.h libsepol/include/sepol/users.h
--- libsepol.new/include/sepol/users.h 2005-09-14 11:44:44.000000000 -0400
+++ libsepol/include/sepol/users.h 2005-09-23 21:55:52.000000000 -0400
@@ -32,10 +32,10 @@
policydb_t* policydb,
const char* role);
-/* Obtain an array of all valid users/roles */
-extern int sepol_get_valid_users(
+/* Obtain an array of all valid users */
+extern int sepol_user_list(
policydb_t* policydb,
- char*** users,
+ sepol_user_t** users,
size_t* nusers);
extern int sepol_get_valid_roles(
@@ -43,4 +43,4 @@
char*** roles,
size_t* nroles);
-#endif /* _SEPOL_USERS_H_ */
+#endif
diff -Nrua libsepol.new/src/booleans.c libsepol/src/booleans.c
--- libsepol.new/src/booleans.c 2005-09-21 10:42:24.000000000 -0400
+++ libsepol/src/booleans.c 2005-09-23 20:42:45.000000000 -0400
@@ -11,7 +11,7 @@
#include <sepol/conditional.h>
#include <sepol/boolean_record.h>
-static inline int bool_update (
+static int bool_update (
policydb_t* policydb,
sepol_bool_t boolean) {
diff -Nrua libsepol.new/src/interfaces.c libsepol/src/interfaces.c
--- libsepol.new/src/interfaces.c 2005-09-21 10:42:24.000000000 -0400
+++ libsepol/src/interfaces.c 2005-09-23 20:43:05.000000000 -0400
@@ -12,7 +12,7 @@
/* Create a low level interface structure from
* a high level representation */
-int sepol_iface_struct_create(
+static int sepol_iface_struct_create(
policydb_t* policydb,
ocontext_t** iface,
sepol_iface_t data) {
diff -Nrua libsepol.new/src/ports.c libsepol/src/ports.c
--- libsepol.new/src/ports.c 2005-08-02 09:17:09.000000000 -0400
+++ libsepol/src/ports.c 2005-09-23 20:42:12.000000000 -0400
@@ -25,7 +25,7 @@
/* Create a low level port structure from
* a high level representation */
-int sepol_port_struct_create(
+static int sepol_port_struct_create(
policydb_t* policydb,
ocontext_t** port,
sepol_port_t data) {
diff -Nrua libsepol.new/src/users.c libsepol/src/users.c
--- libsepol.new/src/users.c 2005-09-21 10:42:24.000000000 -0400
+++ libsepol/src/users.c 2005-09-24 01:01:00.000000000 -0400
@@ -257,8 +257,7 @@
mls_level, name);
goto err;
}
- memcpy(&usrdatum->dfltlevel, &context.range.level[0],
- sizeof(usrdatum->dfltlevel));
+ memcpy(&usrdatum->dfltlevel, &context.range.level[0], sizeof(mls_level_t));
/* MLS range */
context_init(&context);
@@ -274,7 +273,7 @@
mls_range, name);
goto err;
}
- memcpy(&usrdatum->range, &context.range, sizeof(usrdatum->range));
+ memcpy(&usrdatum->range, &context.range, sizeof(mls_range_t));
}
/* If there are no errors, and this is a new user, add the user to policy */
@@ -368,18 +367,80 @@
/* Fill an array with all valid users */
-int sepol_get_valid_users(policydb_t* policydb, char*** users, size_t* nusers) {
+int sepol_user_list(
+ policydb_t* policydb,
+ sepol_user_t** users,
+ size_t* nusers) {
+
size_t tmp_nusers = policydb->p_users.nprim;
- char **tmp_users = (char**) malloc(tmp_nusers * sizeof(char*));
- char **ptr;
+ sepol_user_t* tmp_users =
+ (sepol_user_t*) calloc(tmp_nusers, sizeof(sepol_user_t));
+
+ sepol_user_t* ptr;
size_t i;
if (!tmp_users)
goto omem;
-
+
+ /* For each user */
for (i = 0; i < tmp_nusers; i++) {
- tmp_users[i] = strdup(policydb->p_user_val_to_name[i]);
- if (!tmp_users[i])
- goto omem;
+
+ const char* name = policydb->p_user_val_to_name[i];
+ user_datum_t* usrdatum = policydb->user_val_to_struct[i];
+ ebitmap_t* roles = &(usrdatum->roles.roles);
+ ebitmap_node_t* rnode;
+ unsigned bit;
+
+ if (sepol_user_create(&tmp_users[i]) < 0)
+ goto err;
+
+ if (sepol_user_set_name(tmp_users[i], name) < 0)
+ goto err;
+
+ /* Extract roles */
+ ebitmap_for_each_bit(roles, rnode, bit) {
+ if (ebitmap_node_get_bit(rnode, bit)) {
+ char* role = policydb->p_role_val_to_name[bit];
+ if (sepol_user_add_role(tmp_users[i], role) < 0)
+ goto err;
+ }
+ }
+
+ /* Extract MLS info */
+ if (mls_enabled) {
+ context_struct_t context;
+ char *str;
+ int len;
+
+ context_init(&context);
+ memcpy(&context.range.level[0],
+ &usrdatum->dfltlevel, sizeof(mls_level_t));
+ memcpy(&context.range.level[1],
+ &usrdatum->dfltlevel, sizeof(mls_level_t));
+ len = mls_compute_context_len(policydb, &context);
+ str = (char*) malloc(len);
+ if (str == NULL)
+ goto omem;
+ mls_sid_to_context(policydb, &context, &str);
+ str -= len;
+
+ if ( sepol_user_set_mlslevel(tmp_users[i], str + 1) < 0 ) {
+ free(str);
+ goto err;
+ }
+
+ context_init(&context);
+ memcpy(&context.range, &usrdatum->range, sizeof(mls_range_t));
+ len = mls_compute_context_len(policydb, &context);
+ mls_sid_to_context(policydb, &context, &str);
+ str -= len;
+
+ if ( sepol_user_set_mlsrange(tmp_users[i], str + 1) < 0) {
+ free(str);
+ goto err;
+ }
+
+ free(str);
+ }
}
*nusers = tmp_nusers;
@@ -388,12 +449,14 @@
return STATUS_SUCCESS;
omem:
- DEBUG(__FUNCTION__, "out of memory, could not "
- "allocate list of valid users\n");
+ DEBUG(__FUNCTION__, "out of memory\n");
+
+ err:
+ DEBUG(__FUNCTION__, "could not enumerate users\n");
ptr = tmp_users;
- while (ptr && *ptr)
- free(*ptr++);
+ while (ptr && (*ptr != NULL))
+ sepol_user_free(*ptr++);
free(tmp_users);
return STATUS_ERR;
}
^ permalink raw reply [flat|nested] 7+ messages in thread* Re: [ SEPOL ] Extract user records from binary policy
2005-09-24 5:21 [ SEPOL ] Extract user records from binary policy Ivan Gyurdiev
@ 2005-09-24 5:31 ` Ivan Gyurdiev
2005-09-26 19:19 ` Stephen Smalley
0 siblings, 1 reply; 7+ messages in thread
From: Ivan Gyurdiev @ 2005-09-24 5:31 UTC (permalink / raw)
To: selinux; +Cc: dwalsh
[-- Attachment #1: Type: text/plain, Size: 71 bytes --]
...and here's the same patch, with the obvious malloc bug fixed...tsk
[-- Attachment #2: libsepol.users.list2.diff --]
[-- Type: text/x-patch, Size: 5434 bytes --]
diff -Nrua libsepol.new/include/sepol/users.h libsepol/include/sepol/users.h
--- libsepol.new/include/sepol/users.h 2005-09-14 11:44:44.000000000 -0400
+++ libsepol/include/sepol/users.h 2005-09-23 21:55:52.000000000 -0400
@@ -32,10 +32,10 @@
policydb_t* policydb,
const char* role);
-/* Obtain an array of all valid users/roles */
-extern int sepol_get_valid_users(
+/* Obtain an array of all valid users */
+extern int sepol_user_list(
policydb_t* policydb,
- char*** users,
+ sepol_user_t** users,
size_t* nusers);
extern int sepol_get_valid_roles(
@@ -43,4 +43,4 @@
char*** roles,
size_t* nroles);
-#endif /* _SEPOL_USERS_H_ */
+#endif
diff -Nrua libsepol.new/src/booleans.c libsepol/src/booleans.c
--- libsepol.new/src/booleans.c 2005-09-21 10:42:24.000000000 -0400
+++ libsepol/src/booleans.c 2005-09-23 20:42:45.000000000 -0400
@@ -11,7 +11,7 @@
#include <sepol/conditional.h>
#include <sepol/boolean_record.h>
-static inline int bool_update (
+static int bool_update (
policydb_t* policydb,
sepol_bool_t boolean) {
diff -Nrua libsepol.new/src/interfaces.c libsepol/src/interfaces.c
--- libsepol.new/src/interfaces.c 2005-09-21 10:42:24.000000000 -0400
+++ libsepol/src/interfaces.c 2005-09-23 20:43:05.000000000 -0400
@@ -12,7 +12,7 @@
/* Create a low level interface structure from
* a high level representation */
-int sepol_iface_struct_create(
+static int sepol_iface_struct_create(
policydb_t* policydb,
ocontext_t** iface,
sepol_iface_t data) {
diff -Nrua libsepol.new/src/ports.c libsepol/src/ports.c
--- libsepol.new/src/ports.c 2005-08-02 09:17:09.000000000 -0400
+++ libsepol/src/ports.c 2005-09-23 20:42:12.000000000 -0400
@@ -25,7 +25,7 @@
/* Create a low level port structure from
* a high level representation */
-int sepol_port_struct_create(
+static int sepol_port_struct_create(
policydb_t* policydb,
ocontext_t** port,
sepol_port_t data) {
diff -Nrua libsepol.new/src/users.c libsepol/src/users.c
--- libsepol.new/src/users.c 2005-09-21 10:42:24.000000000 -0400
+++ libsepol/src/users.c 2005-09-24 01:28:27.000000000 -0400
@@ -257,8 +257,7 @@
mls_level, name);
goto err;
}
- memcpy(&usrdatum->dfltlevel, &context.range.level[0],
- sizeof(usrdatum->dfltlevel));
+ memcpy(&usrdatum->dfltlevel, &context.range.level[0], sizeof(mls_level_t));
/* MLS range */
context_init(&context);
@@ -274,7 +273,7 @@
mls_range, name);
goto err;
}
- memcpy(&usrdatum->range, &context.range, sizeof(usrdatum->range));
+ memcpy(&usrdatum->range, &context.range, sizeof(mls_range_t));
}
/* If there are no errors, and this is a new user, add the user to policy */
@@ -368,18 +367,83 @@
/* Fill an array with all valid users */
-int sepol_get_valid_users(policydb_t* policydb, char*** users, size_t* nusers) {
+int sepol_user_list(
+ policydb_t* policydb,
+ sepol_user_t** users,
+ size_t* nusers) {
+
size_t tmp_nusers = policydb->p_users.nprim;
- char **tmp_users = (char**) malloc(tmp_nusers * sizeof(char*));
- char **ptr;
+ sepol_user_t* tmp_users =
+ (sepol_user_t*) calloc(tmp_nusers, sizeof(sepol_user_t));
+
+ sepol_user_t* ptr;
size_t i;
if (!tmp_users)
goto omem;
-
+
+ /* For each user */
for (i = 0; i < tmp_nusers; i++) {
- tmp_users[i] = strdup(policydb->p_user_val_to_name[i]);
- if (!tmp_users[i])
- goto omem;
+
+ const char* name = policydb->p_user_val_to_name[i];
+ user_datum_t* usrdatum = policydb->user_val_to_struct[i];
+ ebitmap_t* roles = &(usrdatum->roles.roles);
+ ebitmap_node_t* rnode;
+ unsigned bit;
+
+ if (sepol_user_create(&tmp_users[i]) < 0)
+ goto err;
+
+ if (sepol_user_set_name(tmp_users[i], name) < 0)
+ goto err;
+
+ /* Extract roles */
+ ebitmap_for_each_bit(roles, rnode, bit) {
+ if (ebitmap_node_get_bit(rnode, bit)) {
+ char* role = policydb->p_role_val_to_name[bit];
+ if (sepol_user_add_role(tmp_users[i], role) < 0)
+ goto err;
+ }
+ }
+
+ /* Extract MLS info */
+ if (mls_enabled) {
+ context_struct_t context;
+ char *str;
+ int len;
+
+ context_init(&context);
+ memcpy(&context.range.level[0],
+ &usrdatum->dfltlevel, sizeof(mls_level_t));
+ memcpy(&context.range.level[1],
+ &usrdatum->dfltlevel, sizeof(mls_level_t));
+ len = mls_compute_context_len(policydb, &context);
+ str = (char*) malloc(len);
+ if (str == NULL)
+ goto omem;
+ mls_sid_to_context(policydb, &context, &str);
+ str -= len;
+
+ if ( sepol_user_set_mlslevel(tmp_users[i], str + 1) < 0 ) {
+ free(str);
+ goto err;
+ }
+ free(str);
+
+ context_init(&context);
+ memcpy(&context.range, &usrdatum->range, sizeof(mls_range_t));
+ len = mls_compute_context_len(policydb, &context);
+ str = (char*) malloc(len);
+ if (str == NULL)
+ goto omem;
+ mls_sid_to_context(policydb, &context, &str);
+ str -= len;
+
+ if ( sepol_user_set_mlsrange(tmp_users[i], str + 1) < 0) {
+ free(str);
+ goto err;
+ }
+ free(str);
+ }
}
*nusers = tmp_nusers;
@@ -388,12 +452,14 @@
return STATUS_SUCCESS;
omem:
- DEBUG(__FUNCTION__, "out of memory, could not "
- "allocate list of valid users\n");
+ DEBUG(__FUNCTION__, "out of memory\n");
+
+ err:
+ DEBUG(__FUNCTION__, "could not enumerate users\n");
ptr = tmp_users;
- while (ptr && *ptr)
- free(*ptr++);
+ while (ptr && (*ptr != NULL))
+ sepol_user_free(*ptr++);
free(tmp_users);
return STATUS_ERR;
}
^ permalink raw reply [flat|nested] 7+ messages in thread* Re: [ SEPOL ] Extract user records from binary policy
2005-09-24 5:31 ` Ivan Gyurdiev
@ 2005-09-26 19:19 ` Stephen Smalley
2005-09-27 1:34 ` Ivan Gyurdiev
0 siblings, 1 reply; 7+ messages in thread
From: Stephen Smalley @ 2005-09-26 19:19 UTC (permalink / raw)
To: Ivan Gyurdiev; +Cc: selinux, dwalsh
On Sat, 2005-09-24 at 01:31 -0400, Ivan Gyurdiev wrote:
> ...and here's the same patch, with the obvious malloc bug fixed...tsk
diff -Nrua libsepol.new/src/users.c libsepol/src/users.c
--- libsepol.new/src/users.c 2005-09-21 10:42:24.000000000 -0400
+++ libsepol/src/users.c 2005-09-24 01:28:27.000000000 -0400
+ /* Extract MLS info */
+ if (mls_enabled) {
+ context_struct_t context;
+ char *str;
+ int len;
+
+ context_init(&context);
+ memcpy(&context.range.level[0],
+ &usrdatum->dfltlevel, sizeof(mls_level_t));
+ memcpy(&context.range.level[1],
+ &usrdatum->dfltlevel, sizeof(mls_level_t));
+ len = mls_compute_context_len(policydb, &context);
+ str = (char*) malloc(len);
+ if (str == NULL)
+ goto omem;
+ mls_sid_to_context(policydb, &context, &str);
+ str -= len;
+
+ if ( sepol_user_set_mlslevel(tmp_users[i], str + 1) < 0 ) {
+ free(str);
+ goto err;
+ }
+ free(str);
+
+ context_init(&context);
+ memcpy(&context.range, &usrdatum->range, sizeof(mls_range_t));
+ len = mls_compute_context_len(policydb, &context);
+ str = (char*) malloc(len);
+ if (str == NULL)
+ goto omem;
+ mls_sid_to_context(policydb, &context, &str);
+ str -= len;
+
+ if ( sepol_user_set_mlsrange(tmp_users[i], str + 1) < 0) {
+ free(str);
+ goto err;
+ }
+ free(str);
+ }
}
I understand that you are trying to just re-use the existing mls_
functions here, but I think it would be better to alter them or add new
ones that provide a saner interface for this kind of manipulation. In
particular, the manipulation of str above makes me nervous, even if it
is technically correct.
--
Stephen Smalley
National Security Agency
--
This message was distributed to subscribers of the selinux mailing list.
If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with
the words "unsubscribe selinux" without quotes as the message.
^ permalink raw reply [flat|nested] 7+ messages in thread* Re: [ SEPOL ] Extract user records from binary policy
2005-09-26 19:19 ` Stephen Smalley
@ 2005-09-27 1:34 ` Ivan Gyurdiev
2005-09-27 18:58 ` Stephen Smalley
0 siblings, 1 reply; 7+ messages in thread
From: Ivan Gyurdiev @ 2005-09-27 1:34 UTC (permalink / raw)
To: Stephen Smalley; +Cc: selinux, dwalsh
>I understand that you are trying to just re-use the existing mls_
>functions here, but I think it would be better to alter them or add new
>ones that provide a saner interface for this kind of manipulation. In
>particular, the manipulation of str above makes me nervous, even if it
>is technically correct.
>
>
Yes, I agree the mls interface should be improved... so I don't have to
write things like:
if (mls_context_to_sid(policydb, '$', &mls_level,
&context)) {
DEBUG(__FUNCTION__, "invalid level %s for user
%s\n",
mls_level, name);
goto err;
}
Is this patch vetoed for now?
--
This message was distributed to subscribers of the selinux mailing list.
If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with
the words "unsubscribe selinux" without quotes as the message.
^ permalink raw reply [flat|nested] 7+ messages in thread* Re: [ SEPOL ] Extract user records from binary policy
2005-09-27 1:34 ` Ivan Gyurdiev
@ 2005-09-27 18:58 ` Stephen Smalley
2005-09-27 19:23 ` Ivan Gyurdiev
0 siblings, 1 reply; 7+ messages in thread
From: Stephen Smalley @ 2005-09-27 18:58 UTC (permalink / raw)
To: Ivan Gyurdiev; +Cc: selinux, dwalsh
On Mon, 2005-09-26 at 21:34 -0400, Ivan Gyurdiev wrote:
> >I understand that you are trying to just re-use the existing mls_
> >functions here, but I think it would be better to alter them or add new
> >ones that provide a saner interface for this kind of manipulation. In
> >particular, the manipulation of str above makes me nervous, even if it
> >is technically correct.
> >
> >
> Yes, I agree the mls interface should be improved... so I don't have to
> write things like:
>
> if (mls_context_to_sid(policydb, '$', &mls_level,
> &context)) {
> DEBUG(__FUNCTION__, "invalid level %s for user
> %s\n",
> mls_level, name);
> goto err;
> }
Yes. The interface did make sense for its original usage, but not for
the way in which it is being used here.
> Is this patch vetoed for now?
Yes.
--
Stephen Smalley
National Security Agency
--
This message was distributed to subscribers of the selinux mailing list.
If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with
the words "unsubscribe selinux" without quotes as the message.
^ permalink raw reply [flat|nested] 7+ messages in thread* Re: [ SEPOL ] Extract user records from binary policy
2005-09-27 18:58 ` Stephen Smalley
@ 2005-09-27 19:23 ` Ivan Gyurdiev
2005-09-27 19:23 ` Stephen Smalley
0 siblings, 1 reply; 7+ messages in thread
From: Ivan Gyurdiev @ 2005-09-27 19:23 UTC (permalink / raw)
To: Stephen Smalley; +Cc: selinux, dwalsh
Stephen Smalley wrote:
>On Mon, 2005-09-26 at 21:34 -0400, Ivan Gyurdiev wrote:
>
>
>>>I understand that you are trying to just re-use the existing mls_
>>>functions here, but I think it would be better to alter them or add new
>>>ones that provide a saner interface for this kind of manipulation. In
>>>particular, the manipulation of str above makes me nervous, even if it
>>>is technically correct.
>>>
>>>
>>>
>>>
>>Yes, I agree the mls interface should be improved... so I don't have to
>>write things like:
>>
>> if (mls_context_to_sid(policydb, '$', &mls_level,
>>&context)) {
>> DEBUG(__FUNCTION__, "invalid level %s for user
>>%s\n",
>> mls_level, name);
>> goto err;
>> }
>>
>>
>
>Yes. The interface did make sense for its original usage, but not for
>the way in which it is being used here.
>
>
Well, I could define a new function which allocates the string (after
computing proper length), writes to it, and returns a valid ptr...
however that's just moving the potential point of failure from two calls
in users.c into one function in mls.c....
Like this?
int sepol_mls_struct_to_string(policydb_t* policydb, context_struct_t*
mls, char** str).
int sepol_mls_struct_from_string(policydb_t* policydb, char* str,
context_struct_t** mls).
--
This message was distributed to subscribers of the selinux mailing list.
If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with
the words "unsubscribe selinux" without quotes as the message.
^ permalink raw reply [flat|nested] 7+ messages in thread* Re: [ SEPOL ] Extract user records from binary policy
2005-09-27 19:23 ` Ivan Gyurdiev
@ 2005-09-27 19:23 ` Stephen Smalley
0 siblings, 0 replies; 7+ messages in thread
From: Stephen Smalley @ 2005-09-27 19:23 UTC (permalink / raw)
To: Ivan Gyurdiev; +Cc: selinux, dwalsh
On Tue, 2005-09-27 at 15:23 -0400, Ivan Gyurdiev wrote:
> Well, I could define a new function which allocates the string (after
> computing proper length), writes to it, and returns a valid ptr...
> however that's just moving the potential point of failure from two calls
> in users.c into one function in mls.c....
>
> Like this?
>
> int sepol_mls_struct_to_string(policydb_t* policydb, context_struct_t*
> mls, char** str).
> int sepol_mls_struct_from_string(policydb_t* policydb, char* str,
> context_struct_t** mls).
Yes, although I think that context_struct_t* is fine in both cases (i.e.
require the caller to provide one), especially as the caller may be
building up a complete context structure and just want the MLS fields
filled in by the mls function.
--
Stephen Smalley
National Security Agency
--
This message was distributed to subscribers of the selinux mailing list.
If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with
the words "unsubscribe selinux" without quotes as the message.
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2005-09-27 19:23 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2005-09-24 5:21 [ SEPOL ] Extract user records from binary policy Ivan Gyurdiev
2005-09-24 5:31 ` Ivan Gyurdiev
2005-09-26 19:19 ` Stephen Smalley
2005-09-27 1:34 ` Ivan Gyurdiev
2005-09-27 18:58 ` Stephen Smalley
2005-09-27 19:23 ` Ivan Gyurdiev
2005-09-27 19:23 ` Stephen Smalley
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.