You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2253 lines
68 KiB
2253 lines
68 KiB
|
|
/* |
|
|
|
Arbitrary Attribute Interchange Protocol , AAIP versions 0.2 , 1.0 , 2.0. |
|
Implementation of encoding and decoding xattr and ACL. |
|
|
|
See libisofs/aaip_0_2.h |
|
http://libburnia-project.org/wiki/AAIP |
|
|
|
Copyright (c) 2009 - 2016 Thomas Schmitt |
|
|
|
This file is part of the libisofs project; you can redistribute it and/or |
|
modify it under the terms of the GNU General Public License version 2 |
|
or later as published by the Free Software Foundation. |
|
See COPYING file for details. |
|
|
|
*/ |
|
|
|
#ifdef HAVE_CONFIG_H |
|
#include "../config.h" |
|
#endif |
|
|
|
#include <ctype.h> |
|
#include <sys/types.h> |
|
#include <unistd.h> |
|
#include <stdlib.h> |
|
#include <string.h> |
|
#include <stdio.h> |
|
#include <pwd.h> |
|
#include <grp.h> |
|
#include <sys/stat.h> |
|
|
|
#include "libisofs.h" |
|
#include "util.h" |
|
|
|
/* |
|
#define Aaip_encode_debuG 1 |
|
*/ |
|
|
|
#include "aaip_0_2.h" |
|
|
|
#define Aaip_EXEC 1 |
|
#define Aaip_WRITE 2 |
|
#define Aaip_READ 4 |
|
|
|
#define Aaip_TRANSLATE 0 |
|
#define Aaip_ACL_USER_OBJ 1 |
|
#define Aaip_ACL_USER 2 |
|
#define Aaip_ACL_GROUP_OBJ 3 |
|
#define Aaip_ACL_GROUP 4 |
|
#define Aaip_ACL_MASK 5 |
|
#define Aaip_ACL_OTHER 6 |
|
#define Aaip_SWITCH_MARK 8 |
|
#define Aaip_ACL_USER_N 10 |
|
#define Aaip_ACL_GROUP_N 12 |
|
#define Aaip_FUTURE_VERSION 15 |
|
|
|
#define Aaip_with_short_namespaceS yes |
|
#define Aaip_max_named_spacE 0x06 |
|
#define Aaip_min_named_spacE 0x02 |
|
#define Aaip_maxdef_namespacE 0x1f |
|
|
|
#define Aaip_namespace_literaL 0x01 |
|
#define Aaip_namespace_systeM 0x02 |
|
#define Aaip_namespace_useR 0x03 |
|
#define Aaip_namespace_isofS 0x04 |
|
#define Aaip_namespace_trusteD 0x05 |
|
#define Aaip_namespace_securitY 0x06 |
|
|
|
/* maximum expansion: "security." */ |
|
#define Aaip_max_name_expansioN 9 |
|
|
|
static char Aaip_namespace_textS[][Aaip_max_name_expansioN + 1]= |
|
{"", "", "system.", "user.", "isofs.", "trusted.", "security."}; |
|
|
|
/* --------------------------------- Encoder ---------------------------- */ |
|
|
|
|
|
static int aaip_encode_pair(char *name, size_t attr_length, char *attr, |
|
unsigned int *num_recs, size_t *comp_size, |
|
unsigned char *result, size_t result_fill, |
|
int flag); |
|
|
|
|
|
/* Convert an array of Arbitrary Attributes into a series of AAIP fields. |
|
@param num_attrs Number of attributes |
|
@param names Array of pointers to 0 terminated name strings |
|
@param value_lengths Array of byte lengths for each value |
|
@param values Array of pointers to the value bytes |
|
@param result_len Number of bytes in the resulting SUSP field string |
|
@param result *result will point to the start of the result string. |
|
This is malloc() memory which needs to be freed when |
|
no longer needed |
|
@param flag Bitfield for control purposes |
|
bit0= set CONTINUE bit of last AAIP field to 1 |
|
@return >= 0 is the number of SUSP fields generated, |
|
< 0 means error |
|
*/ |
|
ssize_t aaip_encode(size_t num_attrs, char **names, |
|
size_t *value_lengths, char **values, |
|
size_t *result_len, unsigned char **result, int flag) |
|
{ |
|
size_t mem_size= 0, comp_size; |
|
ssize_t ret; |
|
unsigned int number_of_fields, i, num_recs; |
|
|
|
/* Predict memory needs, number of SUSP fields and component records */ |
|
*result = NULL; |
|
*result_len= 0; |
|
for(i= 0; i < num_attrs; i++) { |
|
ret= aaip_encode_pair(names[i], value_lengths[i], values[i], |
|
&num_recs, &comp_size, NULL, (size_t) 0, 1); |
|
if(ret < 0) |
|
return(ret); |
|
mem_size+= comp_size; |
|
} |
|
number_of_fields= mem_size / 250 + !!(mem_size % 250); |
|
if(number_of_fields == 0) |
|
return(0); |
|
mem_size+= number_of_fields * 5; |
|
|
|
#ifdef Aaip_encode_debuG |
|
*result= (unsigned char *) calloc(1, mem_size + 1024000); |
|
/* generous honeypot for overflows */ |
|
#else |
|
*result= (unsigned char *) calloc(1, mem_size); |
|
#endif |
|
|
|
if(*result == NULL) |
|
return ISO_OUT_OF_MEM; |
|
|
|
/* Encode pairs into result */ |
|
for(i= 0; i < num_attrs; i++) { |
|
ret= aaip_encode_pair(names[i], value_lengths[i], values[i], |
|
&num_recs, &comp_size, *result, *result_len, 0); |
|
if(ret < 0) { |
|
free(*result); |
|
*result = NULL; |
|
*result_len = 0; |
|
return(ret); |
|
} |
|
(*result_len)+= comp_size; |
|
} |
|
|
|
/* write the field headers */ |
|
for(i= 0; i < number_of_fields; i++) { |
|
(*result)[i * 255 + 0]= 'A'; |
|
(*result)[i * 255 + 1]= 'L'; |
|
if(i < number_of_fields - 1 || (mem_size % 255) == 0) |
|
(*result)[i * 255 + 2]= 255; |
|
else |
|
(*result)[i * 255 + 2]= mem_size % 255; |
|
(*result)[i * 255 + 3]= 1; |
|
(*result)[i * 255 + 4]= (flag & 1) || (i < number_of_fields - 1); |
|
} |
|
(*result_len)+= number_of_fields * 5; |
|
|
|
#ifdef Aaip_encode_debuG |
|
if(*result_len != mem_size) { |
|
fprintf(stderr, "aaip_encode(): MEMORY MISMATCH BY %d BYTES\n", |
|
(int) (mem_size - *result_len)); |
|
} else { |
|
unsigned char *hpt; |
|
hpt= malloc(*result_len); |
|
if(hpt != NULL) { |
|
memcpy(hpt, *result, *result_len); |
|
free(*result); |
|
*result= hpt; |
|
} |
|
} |
|
ret= 0; |
|
for(i= 0; i < *result_len; i+= ((unsigned char *) (*result))[i + 2]) |
|
ret++; |
|
if(ret != (int) number_of_fields) { |
|
fprintf(stderr, "aaip_encode(): WRONG NUMBER OF FIELDS %d <> %d\n", |
|
(int) number_of_fields, ret); |
|
} |
|
#endif /* Aaip_encode_debuG */ |
|
|
|
return(number_of_fields); |
|
} |
|
|
|
|
|
static void aaip_encode_byte(unsigned char *result, size_t *result_fill, |
|
unsigned char value) |
|
{ |
|
result[(*result_fill / 250) * 255 + 5 + (*result_fill % 250)]= value; |
|
(*result_fill)++; |
|
} |
|
|
|
|
|
static int aaip_encode_comp(unsigned char *result, size_t *result_fill, |
|
int prefix, char *data, size_t l, int flag) |
|
{ |
|
size_t todo; |
|
char *rpt, *comp_start; |
|
|
|
if(l == 0 && prefix <= 0) { |
|
aaip_encode_byte(result, result_fill, 0); |
|
aaip_encode_byte(result, result_fill, 0); |
|
return(1); |
|
} |
|
for(rpt= data; rpt - data < (ssize_t) l;) { |
|
todo= l - (rpt - data) + (prefix > 0); |
|
aaip_encode_byte(result, result_fill, (todo > 255)); |
|
if(todo > 255) |
|
todo= 255; |
|
aaip_encode_byte(result, result_fill, todo); |
|
if(prefix > 0) { |
|
aaip_encode_byte(result, result_fill, prefix); |
|
todo--; |
|
prefix= 0; |
|
} |
|
for(comp_start= rpt; rpt - comp_start < (ssize_t) todo; rpt++) |
|
aaip_encode_byte(result, result_fill, *((unsigned char *) rpt)); |
|
} |
|
return(1); |
|
} |
|
|
|
|
|
/* Write the component records for name and attr. Skip the positions of |
|
AAIP field headers. |
|
@param flag bit0= only count but do not produce result |
|
*/ |
|
static int aaip_encode_pair(char *name, size_t attr_length, char *attr, |
|
unsigned int *num_recs, size_t *comp_size, |
|
unsigned char *result, size_t result_fill, |
|
int flag) |
|
{ |
|
size_t l; |
|
int i, prefix= 0; |
|
|
|
#ifdef Aaip_with_short_namespaceS |
|
|
|
/* translate name into eventual short form */ |
|
for(i= Aaip_min_named_spacE; i <= Aaip_max_named_spacE; i++) |
|
if(strncmp(name, Aaip_namespace_textS[i], strlen(Aaip_namespace_textS[i])) |
|
== 0) { |
|
name+= strlen(Aaip_namespace_textS[i]); |
|
prefix= i; |
|
} |
|
/* Eventually prepend escape marker for strange names */ |
|
if(prefix <= 0 && name[0] > 0 && name[0] <= Aaip_maxdef_namespacE) |
|
prefix= Aaip_namespace_literaL; |
|
|
|
#endif /* Aaip_with_short_namespaceS */ |
|
|
|
l= strlen(name) + (prefix > 0); |
|
*num_recs= l / 255 + (!!(l % 255)) + (l == 0) + |
|
attr_length / 255 + (!!(attr_length % 255)) + (attr_length == 0); |
|
*comp_size= l + attr_length + 2 * *num_recs; |
|
|
|
if(flag & 1) |
|
return(1); |
|
|
|
aaip_encode_comp(result, &result_fill, prefix, name, l - (prefix > 0), 0); |
|
aaip_encode_comp(result, &result_fill, 0, attr, attr_length, 0); |
|
return(1); |
|
} |
|
|
|
|
|
/* ----------- Encoder for ACLs ----------- */ |
|
|
|
static ssize_t aaip_encode_acl_text(char *acl_text, mode_t st_mode, |
|
size_t result_size, unsigned char *result, int flag); |
|
|
|
|
|
/* Convert an ACL text as of acl_to_text(3) into the value of an Arbitrary |
|
Attribute. According to AAIP this value is to be stored together with |
|
an empty name. |
|
@param acl_text The ACL in long text form |
|
@param st_mode The stat(2) permission bits to be used with flag bit3 |
|
@param result_len Number of bytes in the resulting value |
|
@param result *result will point to the start of the result string. |
|
This is malloc() memory which needs to be freed when |
|
no longer needed |
|
@param flag Bitfield for control purposes |
|
bit0= count only |
|
bit1= use numeric qualifiers rather than names |
|
bit2= this is a default ACL, prepend SWITCH_MARK |
|
bit3= check for completeness of list and eventually |
|
fill up with entries deduced from st_mode |
|
@return >0 means ok |
|
<=0 means error |
|
-1= out of memory |
|
-2= program error with prediction of result size |
|
-3= error with conversion of name to uid or gid |
|
ISO_AAIP_ACL_MULT_OBJ= multiple entries of user::, group::, other:: |
|
*/ |
|
int aaip_encode_acl(char *acl_text, mode_t st_mode, |
|
size_t *result_len, unsigned char **result, int flag) |
|
{ |
|
ssize_t bytes; |
|
|
|
*result= NULL; |
|
*result_len= 0; |
|
bytes= aaip_encode_acl_text(acl_text, st_mode, |
|
(size_t) 0, NULL, 1 | (flag & (2 | 4 | 8))); |
|
if(bytes < -2) |
|
return(bytes); |
|
if(bytes < 0) |
|
return((int) bytes - 1); |
|
if(flag & 1) { |
|
*result_len= bytes; |
|
return(1); |
|
} |
|
*result= calloc(bytes + 1, 1); |
|
if(*result == NULL) |
|
return(-1); |
|
(*result)[bytes]= 0; |
|
*result_len= bytes; |
|
bytes= aaip_encode_acl_text(acl_text, st_mode, *result_len, *result, |
|
(flag & (2 | 4 | 8))); |
|
if(bytes < -2) |
|
return(bytes); |
|
if(bytes < 0) |
|
return((int) bytes - 1); |
|
if((size_t) bytes != *result_len) { |
|
*result_len= 0; |
|
return(-2); |
|
} |
|
return(1); |
|
} |
|
|
|
|
|
static double aaip_numeric_id(char *name, int flag) |
|
{ |
|
double num; |
|
char *cpt; |
|
|
|
for(cpt= name; *cpt != 0; cpt++) |
|
if(*cpt < '0' || *cpt >'9') |
|
break; |
|
if(*cpt != 0) |
|
return(-1); |
|
sscanf(name, "%lf", &num); |
|
return(num); |
|
} |
|
|
|
|
|
static int aaip_make_aaip_perms(int r, int w, int x) |
|
{ |
|
int perms; |
|
|
|
perms= 0; |
|
if(r) |
|
perms|= Aaip_READ; |
|
if(w) |
|
perms|= Aaip_WRITE; |
|
if(x) |
|
perms|= Aaip_EXEC; |
|
return(perms); |
|
} |
|
|
|
|
|
/* |
|
@param result_size Number of bytes to store result |
|
@param result Pointer to the start of the result string. |
|
@param flag Bitfield for control purposes |
|
bit0= count only, do not really produce bytes |
|
bit1= use numeric qualifiers |
|
bit2= this is a default ACL, prepend SWITCH_MARK 1 |
|
bit3= check for completeness of list and eventually |
|
fill up with entries deduced from st_mode |
|
@return >=0 number of bytes produced resp. counted |
|
<0 means error |
|
-1: result size overflow |
|
-2: conversion errror with user name or group name |
|
ISO_AAIP_ACL_MULT_OBJ: multiple entries of user::, group::, other:: |
|
*/ |
|
static ssize_t aaip_encode_acl_text(char *acl_text, mode_t st_mode, |
|
size_t result_size, unsigned char *result, int flag) |
|
{ |
|
char *rpt, *npt, *cpt; |
|
int qualifier= 0, perms, type, i, qualifier_len= 0, num_recs, needed= 0, ret; |
|
unsigned int has_u= 0, has_g= 0, has_o= 0, has_m= 0, is_trivial= 1; |
|
uid_t uid, huid; |
|
gid_t gid, hgid; |
|
ssize_t count= 0; |
|
struct passwd *pwd; |
|
struct group *grp; |
|
char *name = NULL; |
|
int name_size= 1024; |
|
double num; |
|
|
|
LIBISO_ALLOC_MEM(name, char, name_size); |
|
if(flag & 4) { |
|
/* set SWITCH_MARK to indicate a default ACL */; |
|
if(!(flag & 1)) { |
|
if((size_t) count >= result_size) |
|
{ret= -1; goto ex;} |
|
result[count]= (Aaip_SWITCH_MARK << 4) | Aaip_EXEC; |
|
} |
|
count++; |
|
} |
|
|
|
for(rpt= acl_text; *rpt != 0; rpt= npt) { |
|
npt= strchr(rpt, '\n'); |
|
if(npt == 0) |
|
npt= rpt + strlen(rpt); |
|
else |
|
npt++; |
|
if(*rpt == '#') |
|
continue; |
|
cpt= strchr(rpt, ':'); |
|
if(cpt == NULL) |
|
continue; |
|
cpt= strchr(cpt + 1, ':'); |
|
if(cpt == NULL) |
|
continue; |
|
qualifier= 0; |
|
if(strncmp(rpt, "user:", 5) == 0) { |
|
if(cpt - rpt == 5) { |
|
type= Aaip_ACL_USER_OBJ; |
|
if (has_u) { |
|
|
|
/* >>> Duplicate u:: entry. */; |
|
/* >>> ??? If it matches the previous one: ignore */ |
|
|
|
ret = ISO_AAIP_ACL_MULT_OBJ; |
|
goto ex; |
|
} |
|
has_u++; |
|
} else { |
|
if(cpt - (rpt + 5) >= name_size) |
|
continue; |
|
is_trivial= 0; |
|
strncpy(name, rpt + 5, cpt - (rpt + 5)); |
|
name[cpt - (rpt + 5)]= 0; |
|
if(flag & 2) { |
|
type= Aaip_ACL_USER_N; |
|
pwd= getpwnam(name); |
|
if(pwd == NULL) { |
|
num= aaip_numeric_id(name, 0); |
|
if(num <= 0) { |
|
/* ACL_USER is not part of AAIP 2.0 */ |
|
{ret= -2; goto ex;} |
|
} |
|
uid= huid= num; |
|
} else |
|
uid= huid= pwd->pw_uid; |
|
/* Convert uid into Qualifier Record */ |
|
for(i= 0; huid != 0; i++) |
|
huid= huid >> 8; |
|
qualifier_len= i; |
|
if(qualifier_len <= 0) |
|
qualifier_len= 1; |
|
for(i= 0; i < qualifier_len ; i++) |
|
name[i]= uid >> (8 * (qualifier_len - i - 1)); |
|
} else { |
|
type= Aaip_ACL_USER; |
|
qualifier_len= strlen(name); |
|
if(qualifier_len <= 0) |
|
qualifier_len= 1; |
|
} |
|
qualifier= 1; |
|
} |
|
} else if(strncmp(rpt, "group:", 6) == 0) { |
|
if(cpt - rpt == 6) { |
|
type= Aaip_ACL_GROUP_OBJ; |
|
if (has_g) { |
|
|
|
/* >>> Duplicate g:: entry. */; |
|
/* >>> ??? If it matches the previous one: ignore */ |
|
|
|
ret = ISO_AAIP_ACL_MULT_OBJ; |
|
goto ex; |
|
} |
|
has_g++; |
|
} else { |
|
if(cpt - (rpt + 6) >= name_size) |
|
continue; |
|
is_trivial= 0; |
|
strncpy(name, rpt + 6, cpt - (rpt + 6)); |
|
name[cpt - (rpt + 6)]= 0; |
|
if(flag & 2) { |
|
type= Aaip_ACL_GROUP_N; |
|
grp= getgrnam(name); |
|
if(grp == NULL) { |
|
num= aaip_numeric_id(name, 0); |
|
if(num <= 0) { |
|
/* ACL_GROUP is not part of AAIP 2.0 */ |
|
{ret= -2; goto ex;} |
|
} |
|
gid= hgid= num; |
|
} else |
|
gid= hgid= grp->gr_gid; |
|
/* Convert gid into Qualifier Record */ |
|
for(i= 0; hgid != 0; i++) |
|
hgid= hgid >> 8; |
|
qualifier_len= i; |
|
if(qualifier_len <= 0) |
|
qualifier_len= 1; |
|
for(i= 0; i < qualifier_len ; i++) |
|
name[i]= gid >> (8 * (qualifier_len - i - 1)); |
|
} else { |
|
type= Aaip_ACL_GROUP; |
|
qualifier_len= strlen(name); |
|
if(qualifier_len <= 0) |
|
qualifier_len= 1; |
|
} |
|
qualifier= 1; |
|
} |
|
} else if(strncmp(rpt, "other:", 6) == 0) { |
|
type= Aaip_ACL_OTHER; |
|
if (has_o) { |
|
|
|
/* >>> Duplicate o:: entry. */; |
|
/* >>> ??? If it matches the previous one: ignore */ |
|
|
|
ret = ISO_AAIP_ACL_MULT_OBJ; |
|
goto ex; |
|
} |
|
has_o++; |
|
} else if(strncmp(rpt, "mask:", 5) == 0) { |
|
type= Aaip_ACL_MASK; |
|
has_m++; |
|
} else |
|
continue; |
|
|
|
if(npt - cpt < 4) |
|
continue; |
|
perms= aaip_make_aaip_perms(cpt[1] == 'r', cpt[2] == 'w', cpt[3] == 'x'); |
|
|
|
if(!(flag & 1)) { |
|
if((size_t) count >= result_size) |
|
{ret= -1; goto ex;} |
|
result[count]= perms | ((!!qualifier) << 3) | (type << 4); |
|
} |
|
count++; |
|
|
|
if(qualifier) { |
|
num_recs= (qualifier_len / 127) + !!(qualifier_len % 127); |
|
if(!(flag & 1)) { |
|
if((size_t) (count + 1) > result_size) |
|
{ret= -1; goto ex;} |
|
for(i= 0; i < num_recs; i++) { |
|
if(i < num_recs - 1) |
|
result[count++]= 255; |
|
else { |
|
result[count++]= (qualifier_len % 127); |
|
if(result[count - 1] == 0) |
|
result[count - 1]= 127; |
|
} |
|
if((size_t) (count + (result[count - 1] & 127)) > result_size) |
|
{ret= -1; goto ex;} |
|
memcpy(result + count, name + i * 127, result[count - 1] & 127); |
|
count+= result[count - 1] & 127; |
|
} |
|
} else |
|
count+= qualifier_len + num_recs; |
|
} |
|
} |
|
if (flag & 8) { |
|
/* add eventually missing mandatory ACL entries */ |
|
needed= (!has_u) + (!has_g) + (!has_o) + !(is_trivial || has_m); |
|
if(flag & 1) |
|
count+= needed; |
|
else { |
|
if((size_t) (count + needed) > result_size) |
|
{ret= -1; goto ex;} |
|
} |
|
} |
|
if ((flag & 8) && needed > 0 && !(flag & 1)) { |
|
if(!has_u) { |
|
perms= aaip_make_aaip_perms(st_mode & S_IRUSR, st_mode & S_IWUSR, |
|
st_mode * S_IXUSR); |
|
result[count++]= perms | (Aaip_ACL_USER_OBJ << 4); |
|
} |
|
if(!has_g) { |
|
perms= aaip_make_aaip_perms(st_mode & S_IRGRP, st_mode & S_IWGRP, |
|
st_mode * S_IXGRP); |
|
result[count++]= perms | (Aaip_ACL_GROUP_OBJ << 4); |
|
} |
|
if(!has_o) { |
|
perms= aaip_make_aaip_perms(st_mode & S_IROTH, st_mode & S_IWOTH, |
|
st_mode * S_IXOTH); |
|
result[count++]= perms | (Aaip_ACL_OTHER << 4); |
|
} |
|
if(!(is_trivial | has_m)) { |
|
perms= aaip_make_aaip_perms(st_mode & S_IRGRP, st_mode & S_IWGRP, |
|
st_mode * S_IXGRP); |
|
result[count++]= perms | (Aaip_ACL_MASK << 4); |
|
} |
|
} |
|
ret= count; |
|
ex:; |
|
LIBISO_FREE_MEM(name); |
|
return(ret); |
|
} |
|
|
|
|
|
int aaip_encode_both_acl(char *a_acl_text, char *d_acl_text, mode_t st_mode, |
|
size_t *result_len, unsigned char **result, int flag) |
|
{ |
|
int ret; |
|
size_t a_acl_len= 0, d_acl_len= 0, acl_len= 0; |
|
unsigned char *a_acl= NULL, *d_acl= NULL, *acl= NULL; |
|
|
|
if(a_acl_text != NULL) { |
|
ret= aaip_encode_acl(a_acl_text, st_mode, &a_acl_len, &a_acl, flag & 11); |
|
if(ret <= 0) |
|
goto ex; |
|
} |
|
if(d_acl_text != NULL) { |
|
ret= aaip_encode_acl(d_acl_text, (mode_t) 0, &d_acl_len, &d_acl, |
|
(flag & 3) | 4); |
|
if(ret <= 0) |
|
goto ex; |
|
} |
|
if(a_acl == NULL || a_acl_len == 0) { |
|
acl= d_acl; |
|
d_acl= NULL; |
|
acl_len= d_acl_len; |
|
} else if (d_acl == NULL || d_acl_len == 0) { |
|
acl= a_acl; |
|
a_acl= NULL; |
|
acl_len= a_acl_len; |
|
} else { |
|
acl= calloc(a_acl_len + d_acl_len, 1); |
|
if(acl == NULL) |
|
{ret = -1; goto ex;} |
|
memcpy(acl, a_acl, a_acl_len); |
|
memcpy(acl + a_acl_len, d_acl, d_acl_len); |
|
acl_len= a_acl_len + d_acl_len; |
|
} |
|
*result= acl; |
|
*result_len= acl_len; |
|
ret= 1; |
|
ex:; |
|
if(a_acl != NULL) |
|
free(a_acl); |
|
if(d_acl != NULL) |
|
free(d_acl); |
|
return(ret); |
|
} |
|
|
|
|
|
/* GNU/Linux man 5 acl says: |
|
The permissions defined by ACLs are a superset of the permissions speci- |
|
fied by the file permission bits. The permissions defined for the file |
|
owner correspond to the permissions of the ACL_USER_OBJ entry. The per- |
|
missions defined for the file group correspond to the permissions of the |
|
ACL_GROUP_OBJ entry, if the ACL has no ACL_MASK entry. If the ACL has an |
|
ACL_MASK entry, then the permissions defined for the file group corre- |
|
spond to the permissions of the ACL_MASK entry. The permissions defined |
|
for the other class correspond to the permissions of the ACL_OTHER_OBJ |
|
entry. |
|
|
|
Modification of the file permission bits results in the modification of |
|
the permissions in the associated ACL entries. Modification of the per- |
|
missions in the ACL entries results in the modification of the file per- |
|
mission bits. |
|
|
|
*/ |
|
/* Analyze occurrence of ACL tag types in long text form. If not disabled by |
|
parameter flag remove the entries of type "user::" , "group::" , "other::" , |
|
or "other:" from an ACL in long text form if they match the bits in st_mode |
|
as described by man 2 stat and man 5 acl. |
|
@param acl_text The text to be analyzed and eventually shortened. |
|
@param st_mode The component of struct stat which tells POSIX permission |
|
bits and eventually shall take equivalent bits as read |
|
from the ACL. The caller should submit a pointer |
|
to the st_mode variable which holds permissions as |
|
indicated by stat(2) resp. ECMA-119 and RRIP data. |
|
@param flag bit0= do not remove entries, only determine return value |
|
bit1= like bit0 but return immediately if a non-st_mode |
|
ACL entry is found |
|
bit2= update *st_mode by acl_text |
|
("user::" -> S_IRWXU, "mask::"|"group::" -> S_IRWXG, |
|
"other::" -> S_IRWXO) |
|
bit3= update acl_text by *st_mode (same mapping as bit 2 |
|
but with reversed transfer direction) |
|
bit4= map "group::" <-> S_IRWXG in any case. |
|
I.e. ignore "mask::". |
|
@return <0 failure |
|
>=0 tells in its bits which tag types were found. |
|
The first three tell which types deviate from the |
|
corresponding st_mode settings: |
|
bit0= "other::" overrides S_IRWXO |
|
bit1= "group::" overrides S_IRWXG (no "mask::" found) |
|
bit2= "user::" overrides S_IRWXU |
|
The second three tell which types comply with st_mode: |
|
bit3= "other::" matches S_IRWXO |
|
bit4= "group::" matches S_IRWXG (no "mask::" found) |
|
bit5= "user::" matches S_IRWXU |
|
Given the nature of ACLs nearly all combinations are |
|
possible although some would come from invalid ACLs. |
|
bit6= other ACL tag types are present. Particularly: |
|
bit7= "user:...:" is present |
|
bit8= "group:...:" is present |
|
bit9= "mask::" is present |
|
*/ |
|
int aaip_cleanout_st_mode(char *acl_text, mode_t *in_st_mode, int flag) |
|
{ |
|
char *rpt, *wpt, *npt, *cpt; |
|
mode_t m, list_mode, st_mode; |
|
int tag_types= 0, has_mask= 0, do_cleanout = 0; |
|
|
|
list_mode= st_mode= *in_st_mode; |
|
do_cleanout = !(flag & 15); |
|
|
|
has_mask= strncmp(acl_text, "mask:", 5) == 0 || |
|
strstr(acl_text, "\nmask:") != NULL; |
|
if(has_mask && (flag & 2)) |
|
return(64 | 512); |
|
|
|
for(npt= wpt= rpt= acl_text; *npt != 0; rpt= npt + 1) { |
|
npt= strchr(rpt, '\n'); |
|
if(npt == NULL) |
|
npt= rpt + strlen(rpt); |
|
if(strncmp(rpt, "user:", 5) == 0) { |
|
if(rpt[5] == ':' && npt - rpt == 9) { |
|
cpt= rpt + 6; |
|
m= 0; |
|
if(cpt[0] == 'r') |
|
m|= S_IRUSR; |
|
if(cpt[1] == 'w') |
|
m|= S_IWUSR; |
|
if(cpt[2] == 'x') |
|
m|= S_IXUSR; |
|
list_mode= (list_mode & ~S_IRWXU) | m; |
|
if((st_mode & S_IRWXU) == (m & S_IRWXU)) { |
|
tag_types|= 32; |
|
continue; |
|
} |
|
if(flag & 8) { |
|
cpt[0]= st_mode & S_IRUSR ? 'r' : '-'; |
|
cpt[1]= st_mode & S_IWUSR ? 'w' : '-'; |
|
cpt[2]= st_mode & S_IXUSR ? 'x' : '-'; |
|
} |
|
tag_types|= 4; |
|
} else { |
|
tag_types|= 64 | 128; |
|
} |
|
} else if(strncmp(rpt, "group:", 6) == 0) { |
|
if(rpt[6] == ':' && npt - rpt == 10 && ((flag & 16) || !has_mask)) { |
|
/* oddly: mask overrides group in st_mode */ |
|
cpt= rpt + 7; |
|
m= 0; |
|
if(cpt[0] == 'r') |
|
m|= S_IRGRP; |
|
if(cpt[1] == 'w') |
|
m|= S_IWGRP; |
|
if(cpt[2] == 'x') |
|
m|= S_IXGRP; |
|
list_mode= (list_mode & ~S_IRWXG) | m; |
|
if((st_mode & S_IRWXG) == (m & S_IRWXG)) { |
|
tag_types|= 16; |
|
continue; |
|
} |
|
if(flag & 8) { |
|
cpt[0]= st_mode & S_IRGRP ? 'r' : '-'; |
|
cpt[1]= st_mode & S_IWGRP ? 'w' : '-'; |
|
cpt[2]= st_mode & S_IXGRP ? 'x' : '-'; |
|
} |
|
tag_types|= 2; |
|
} else { |
|
if(rpt[6] == ':' && npt - rpt == 10) |
|
tag_types|= 1024; |
|
else |
|
tag_types|= 64 | 256; |
|
} |
|
} else if(strncmp(rpt, "other::", 7) == 0 && npt - rpt == 10) { |
|
cpt= rpt + 7; |
|
others_st_mode:; |
|
m= 0; |
|
if(cpt[0] == 'r') |
|
m|= S_IROTH; |
|
if(cpt[1] == 'w') |
|
m|= S_IWOTH; |
|
if(cpt[2] == 'x') |
|
m|= S_IXOTH; |
|
list_mode= (list_mode & ~S_IRWXO) | m; |
|
if((st_mode & S_IRWXO) == (m & S_IRWXO)) { |
|
tag_types|= 8; |
|
continue; |
|
} |
|
if(flag & 8) { |
|
cpt[0]= st_mode & S_IROTH ? 'r' : '-'; |
|
cpt[1]= st_mode & S_IWOTH ? 'w' : '-'; |
|
cpt[2]= st_mode & S_IXOTH ? 'x' : '-'; |
|
} |
|
tag_types|= 1; |
|
} else if(strncmp(rpt, "other:", 6) == 0 && npt - rpt == 9) { |
|
cpt= rpt + 7; |
|
goto others_st_mode; |
|
} else if(strncmp(rpt, "mask::", 6) == 0 && npt - rpt == 9) { |
|
cpt= rpt + 6; |
|
mask_st_mode:; |
|
tag_types|= 64 | 512; |
|
if(!(flag & 16)) { |
|
/* oddly: mask overrides group in st_mode */ |
|
m= 0; |
|
if(cpt[0] == 'r') |
|
m|= S_IRGRP; |
|
if(cpt[1] == 'w') |
|
m|= S_IWGRP; |
|
if(cpt[2] == 'x') |
|
m|= S_IXGRP; |
|
list_mode= (list_mode & ~S_IRWXG) | m; |
|
if(flag & 8) { |
|
cpt[0]= st_mode & S_IRGRP ? 'r' : '-'; |
|
cpt[1]= st_mode & S_IWGRP ? 'w' : '-'; |
|
cpt[2]= st_mode & S_IXGRP ? 'x' : '-'; |
|
} |
|
} |
|
} else if(strncmp(rpt, "mask:", 5) == 0 && npt - rpt == 8) { |
|
cpt= rpt + 5; |
|
goto mask_st_mode; |
|
} else if(*rpt != 0) { |
|
tag_types|= 64; |
|
} |
|
if (flag & 2) |
|
goto ex; |
|
if(wpt == rpt) { |
|
wpt= npt + 1; |
|
continue; |
|
} |
|
if(do_cleanout) |
|
memmove(wpt, rpt, 1 + npt - rpt); |
|
wpt+= 1 + npt - rpt; |
|
} |
|
if(do_cleanout) { |
|
if(wpt == acl_text) |
|
*wpt= 0; |
|
else if(*(wpt - 1) != 0) |
|
*wpt= 0; |
|
} |
|
ex:; |
|
if(flag & 4) |
|
*in_st_mode= list_mode; |
|
return(tag_types); |
|
} |
|
|
|
|
|
/* Important: acl_text must provide 42 bytes more than its current length ! |
|
*/ |
|
int aaip_add_acl_st_mode(char *acl_text, mode_t st_mode, int flag) |
|
{ |
|
char *wpt; |
|
int tag_types= 0; |
|
|
|
tag_types = aaip_cleanout_st_mode(acl_text, &st_mode, 1); |
|
if(!(tag_types & (4 | 32))) { |
|
wpt= acl_text + strlen(acl_text); |
|
sprintf(wpt, "user::%c%c%c\n", |
|
st_mode & S_IRUSR ? 'r' : '-', |
|
st_mode & S_IWUSR ? 'w' : '-', |
|
st_mode & S_IXUSR ? 'x' : '-'); |
|
} |
|
if(!(tag_types & (2 | 16 | 1024))) { |
|
wpt= acl_text + strlen(acl_text); |
|
sprintf(wpt, "group::%c%c%c\n", |
|
st_mode & S_IRGRP ? 'r' : '-', |
|
st_mode & S_IWGRP ? 'w' : '-', |
|
st_mode & S_IXGRP ? 'x' : '-'); |
|
} |
|
if(!(tag_types & (1 | 8))) { |
|
wpt= acl_text + strlen(acl_text); |
|
sprintf(wpt, "other::%c%c%c\n", |
|
st_mode & S_IROTH ? 'r' : '-', |
|
st_mode & S_IWOTH ? 'w' : '-', |
|
st_mode & S_IXOTH ? 'x' : '-'); |
|
} |
|
if((tag_types & (128 | 256)) && !(tag_types & 512)) { |
|
wpt= acl_text + strlen(acl_text); |
|
sprintf(wpt, "mask::%c%c%c\n", |
|
st_mode & S_IRGRP ? 'r' : '-', |
|
st_mode & S_IWGRP ? 'w' : '-', |
|
st_mode & S_IXGRP ? 'x' : '-'); |
|
} |
|
return(1); |
|
} |
|
|
|
|
|
/* --------------------------------- Decoder ---------------------------- */ |
|
|
|
/* --- private --- */ |
|
|
|
/* Not less than 2 * 2048 */ |
|
#define Aaip_buffer_sizE 4096 |
|
|
|
/* Enough for one full component record and three empty ones which might get |
|
added in case of unclean end of attribute list. |
|
*/ |
|
#define Aaip_buffer_reservE (257 + 3 * 2) |
|
|
|
|
|
struct aaip_state { |
|
|
|
/* AAIP field status */ |
|
int aa_head_missing; /* number of bytes needed to complete field header */ |
|
int aa_missing; /* number of bytes needed to complete current field */ |
|
int aa_ends; /* 0= still fields expected, 1= last field being processed, |
|
2= all fields processed, 3= all is delivered */ |
|
|
|
/* Buffer for component records */ |
|
int recs_invalid; /* number of components to skip */ |
|
unsigned char recs[Aaip_buffer_sizE + Aaip_buffer_reservE]; |
|
size_t recs_fill; |
|
unsigned char *recs_start; |
|
int rec_head_missing; /* number of bytes needed to complete rec header */ |
|
int rec_missing; /* number of bytes needed to complete current rec */ |
|
int rec_ends; |
|
|
|
/* Counter for completed data */ |
|
unsigned int num_recs; |
|
size_t ready_bytes; |
|
|
|
/* Counter and meaning for completed components */ |
|
unsigned int num_components; |
|
size_t end_of_components; /* start index of eventual incomplete component */ |
|
int first_is_name; |
|
|
|
/* Last return value of aaip_decode_pair() */ |
|
int pair_status; |
|
unsigned int pairs_skipped; |
|
|
|
/* status of aaip_decode_attrs() */ |
|
size_t list_mem_used; |
|
size_t list_size; |
|
size_t list_num_attrs; |
|
char **list_names; |
|
size_t *list_value_lengths; |
|
char **list_values; |
|
char *name_buf; |
|
size_t name_buf_size; |
|
size_t name_buf_fill; |
|
char *value_buf; |
|
size_t value_buf_size; |
|
size_t value_buf_fill; |
|
int list_pending_pair; |
|
}; |
|
|
|
|
|
/* ------- functions ------ */ |
|
|
|
|
|
size_t aaip_count_bytes(unsigned char *data, int flag) |
|
{ |
|
int done = 0; |
|
unsigned char *aapt; |
|
|
|
for(aapt= data; !done; aapt += aapt[2]) |
|
done = !(aapt[4] & 1); |
|
return((size_t) (aapt - data)); |
|
} |
|
|
|
|
|
size_t aaip_sizeof_aaip_state(void) |
|
{ |
|
return((size_t) sizeof(struct aaip_state)); |
|
} |
|
|
|
|
|
int aaip_init_aaip_state(struct aaip_state *aaip, int flag) |
|
{ |
|
aaip->aa_head_missing= 5; |
|
aaip->aa_missing= 0; |
|
|
|
aaip->recs_invalid= 0; |
|
memset(aaip->recs, 0, Aaip_buffer_sizE + Aaip_buffer_reservE); |
|
aaip->recs_fill= 0; |
|
aaip->recs_start= aaip->recs; |
|
aaip->rec_head_missing= 2; |
|
aaip->rec_missing= 0; |
|
aaip->rec_ends= 0; |
|
|
|
aaip->num_recs= 0; |
|
aaip->ready_bytes= 0; |
|
|
|
aaip->num_components= 0; |
|
aaip->end_of_components= 0; |
|
aaip->first_is_name= 1; |
|
|
|
aaip->pair_status= 2; |
|
aaip->pairs_skipped= 0; |
|
|
|
aaip->list_mem_used= 0; |
|
aaip->list_size= 0; |
|
aaip->list_num_attrs= 0; |
|
aaip->list_names= NULL; |
|
aaip->list_value_lengths= NULL; |
|
aaip->list_values= NULL; |
|
aaip->name_buf= NULL; |
|
aaip->name_buf_size= 0; |
|
aaip->name_buf_fill= 0; |
|
aaip->value_buf= NULL; |
|
aaip->value_buf_size= 0; |
|
aaip->value_buf_fill= 0; |
|
aaip->list_pending_pair= 0; |
|
return(1); |
|
} |
|
|
|
/* |
|
*/ |
|
#define Aaip_with_ring_buffeR yes |
|
|
|
#ifdef Aaip_with_ring_buffeR |
|
|
|
/* Compute the one or two byte intervals in the ring buffer which form a |
|
given byte interval in the virtual shift fifo. |
|
@param idx The byte start index in the virtual shift fifo. |
|
@param todo Number of bytes to cover |
|
@param start_pt Will return the start address of the first interval |
|
@param at_start_pt Will return the size of the first interval |
|
@param at_recs Will return the size of the second interval which |
|
always starts at aaip->recs |
|
@param flag Bitfield for control purposes |
|
@return 1= next start_pt is *start_pt + *at_start_pt |
|
2= next start_pt is aaip->recs + *at_recs |
|
*/ |
|
static int aaip_ring_adr(struct aaip_state *aaip, size_t idx, size_t todo, |
|
unsigned char **start_pt, size_t *at_start_pt, |
|
size_t *at_recs, int flag) |
|
{ |
|
size_t ahead; |
|
|
|
ahead= Aaip_buffer_sizE + Aaip_buffer_reservE |
|
- (aaip->recs_start - aaip->recs); |
|
if(idx < ahead) |
|
*start_pt= (aaip->recs_start + idx); |
|
else |
|
*start_pt= aaip->recs + (idx - ahead); |
|
ahead= Aaip_buffer_sizE + Aaip_buffer_reservE - (*start_pt - aaip->recs); |
|
if(todo >= ahead) { |
|
*at_start_pt= ahead; |
|
*at_recs= todo - ahead; |
|
return(2); |
|
} |
|
*at_start_pt= todo; |
|
*at_recs= 0; |
|
return(1); |
|
} |
|
|
|
|
|
/* |
|
@param flag Bitfield for control purposes |
|
bit0= count as ready_bytes |
|
*/ |
|
static int aaip_push_to_recs(struct aaip_state *aaip, unsigned char *data, |
|
size_t todo, int flag) |
|
{ |
|
unsigned char *start_pt; |
|
size_t at_start_pt, at_recs; |
|
|
|
aaip_ring_adr(aaip, aaip->recs_fill, todo, |
|
&start_pt, &at_start_pt, &at_recs, 0); |
|
if(at_start_pt > 0) |
|
memcpy(start_pt, data, at_start_pt); |
|
if(at_recs > 0) |
|
memcpy(aaip->recs, data + at_start_pt, at_recs); |
|
aaip->recs_fill+= todo; |
|
if(flag & 1) |
|
aaip->ready_bytes+= todo; |
|
return(1); |
|
} |
|
|
|
|
|
static int aaip_read_from_recs(struct aaip_state *aaip, size_t idx, |
|
unsigned char *data, size_t num_data, int flag) |
|
{ |
|
unsigned char *start_pt; |
|
size_t at_start_pt, at_recs; |
|
|
|
aaip_ring_adr(aaip, idx, num_data, |
|
&start_pt, &at_start_pt, &at_recs, 0); |
|
if(at_start_pt > 0) |
|
memcpy(data, start_pt, at_start_pt); |
|
if(at_recs > 0) |
|
memcpy(data + at_start_pt, aaip->recs, at_recs); |
|
return(1); |
|
} |
|
|
|
|
|
static int aaip_set_buffer_byte(struct aaip_state *aaip, size_t idx, |
|
unsigned char data, int flag) |
|
{ |
|
unsigned char *start_pt; |
|
size_t at_start_pt, at_recs; |
|
|
|
aaip_ring_adr(aaip, idx, 1, |
|
&start_pt, &at_start_pt, &at_recs, 0); |
|
*start_pt= data; |
|
return(1); |
|
} |
|
|
|
|
|
static int aaip_get_buffer_byte(struct aaip_state *aaip, size_t idx, int flag) |
|
{ |
|
unsigned char *start_pt; |
|
size_t at_start_pt, at_recs; |
|
|
|
aaip_ring_adr(aaip, idx, 1, |
|
&start_pt, &at_start_pt, &at_recs, 0); |
|
return((int) *start_pt); |
|
} |
|
|
|
|
|
static int aaip_shift_recs(struct aaip_state *aaip, size_t todo, int flag) |
|
{ |
|
int ret; |
|
unsigned char *start_pt; |
|
size_t at_start_pt, at_recs; |
|
|
|
if(todo < aaip->recs_fill) { |
|
ret= aaip_ring_adr(aaip, 0, todo, &start_pt, &at_start_pt, &at_recs, 0); |
|
if(ret == 1) |
|
aaip->recs_start= start_pt + todo; |
|
else |
|
aaip->recs_start= aaip->recs + at_recs; |
|
} else { |
|
aaip->recs_start= aaip->recs; |
|
} |
|
aaip->recs_fill-= todo; |
|
if(aaip->end_of_components >= todo) |
|
aaip->end_of_components-= todo; |
|
else |
|
aaip->end_of_components= 0; |
|
return(1); |
|
} |
|
|
|
|
|
#else /* Aaip_with_ring_buffeR */ |
|
|
|
|
|
/* |
|
@param flag Bitfield for control purposes |
|
bit0= count as ready_bytes |
|
*/ |
|
static int aaip_push_to_recs(struct aaip_state *aaip, unsigned char *data, |
|
size_t todo, int flag) |
|
{ |
|
memcpy(aaip->recs + aaip->recs_fill, data, todo); |
|
aaip->recs_fill+= todo; |
|
if(flag & 1) |
|
aaip->ready_bytes+= todo; |
|
return(1); |
|
} |
|
|
|
|
|
static int aaip_read_from_recs(struct aaip_state *aaip, size_t idx, |
|
unsigned char *data, size_t num_data, int flag) |
|
{ |
|
memcpy(data, aaip->recs + idx, num_data); |
|
return(1); |
|
} |
|
|
|
|
|
static int aaip_set_buffer_byte(struct aaip_state *aaip, size_t idx, |
|
unsigned char data, int flag) |
|
{ |
|
aaip->recs[idx]= data; |
|
return(1); |
|
} |
|
|
|
|
|
static int aaip_get_buffer_byte(struct aaip_state *aaip, size_t idx, int flag) |
|
{ |
|
return((int) aaip->recs[idx]); |
|
} |
|
|
|
|
|
static int aaip_shift_recs(struct aaip_state *aaip, size_t todo, int flag) |
|
{ |
|
if(todo < aaip->recs_fill) |
|
memmove(aaip->recs, aaip->recs + todo, aaip->recs_fill - todo); |
|
aaip->recs_fill-= todo; |
|
|
|
if(aaip->end_of_components >= todo) |
|
aaip->end_of_components-= todo; |
|
else |
|
aaip->end_of_components= 0; |
|
return(1); |
|
} |
|
|
|
|
|
#endif /* ! Aaip_with_ring_buffeR */ |
|
|
|
|
|
static int aaip_consume_rec_head(struct aaip_state *aaip, |
|
unsigned char **data, size_t *num_data, int flag) |
|
{ |
|
size_t todo; |
|
|
|
todo= *num_data; |
|
if(todo > (size_t) aaip->aa_missing) |
|
todo= aaip->aa_missing; |
|
if(todo >= (size_t) aaip->rec_head_missing) |
|
todo= aaip->rec_head_missing; |
|
if(!aaip->recs_invalid) |
|
aaip_push_to_recs(aaip, *data, todo, 0); |
|
aaip->rec_head_missing-= todo; |
|
if(aaip->rec_head_missing == 0) { |
|
aaip->rec_missing= aaip_get_buffer_byte(aaip, aaip->recs_fill - 1, 0); |
|
aaip->rec_ends= !(aaip_get_buffer_byte(aaip, aaip->recs_fill - 2, 0) & 1); |
|
} |
|
aaip->aa_missing-= todo; |
|
(*num_data)-= todo; |
|
(*data)+= todo; |
|
return(1); |
|
} |
|
|
|
|
|
static int aaip_consume_rec_data(struct aaip_state *aaip, |
|
unsigned char **data, size_t *num_data, int flag) |
|
{ |
|
size_t todo; |
|
|
|
todo= *num_data; |
|
if(todo > (size_t) aaip->aa_missing) |
|
todo= aaip->aa_missing; |
|
if(todo > (size_t) aaip->rec_missing) |
|
todo= aaip->rec_missing; |
|
if(!aaip->recs_invalid) |
|
aaip_push_to_recs(aaip, *data, todo, 1); |
|
aaip->rec_missing-= todo; |
|
aaip->aa_missing-= todo; |
|
(*num_data)-= todo; |
|
(*data)+= todo; |
|
if(aaip->rec_missing <= 0) { |
|
if(aaip->recs_invalid > 0) { |
|
if(aaip->rec_ends) |
|
aaip->recs_invalid--; |
|
} else { |
|
aaip->num_recs++; |
|
if(aaip->rec_ends) { |
|
aaip->num_components++; |
|
aaip->end_of_components= aaip->recs_fill; |
|
} |
|
} |
|
aaip->rec_head_missing= 2; |
|
} |
|
return(0); |
|
} |
|
|
|
|
|
static int aaip_consume_aa_head(struct aaip_state *aaip, |
|
unsigned char **data, size_t *num_data, int flag) |
|
{ |
|
size_t todo; |
|
unsigned char aa_head[5]; |
|
|
|
todo= *num_data; |
|
if(todo >= (size_t) aaip->aa_head_missing) |
|
todo= aaip->aa_head_missing; |
|
aaip_push_to_recs(aaip, *data, todo, 0); |
|
aaip->aa_head_missing-= todo; |
|
if(aaip->aa_head_missing == 0) { |
|
aaip_read_from_recs(aaip, aaip->recs_fill - 5, aa_head, 5, 0); |
|
if(aa_head[0] != 'A' || (aa_head[1] != 'L' && aa_head[1] != 'A') || |
|
aa_head[3] != 1) |
|
return(-1); |
|
aaip->aa_missing= aa_head[2]; |
|
aaip->aa_ends= !(aa_head[4] & 1); |
|
aaip->recs_fill-= 5; /* AAIP field heads do not get delivered */ |
|
if(aaip->aa_missing >= 5) |
|
aaip->aa_missing-= 5; |
|
else |
|
aaip->aa_missing= 0; |
|
} |
|
(*num_data)-= todo; |
|
(*data)+= todo; |
|
return(1); |
|
} |
|
|
|
|
|
static int aaip_consume_aa_data(struct aaip_state *aaip, |
|
unsigned char **data, size_t *num_data, int flag) |
|
{ |
|
size_t i; |
|
static unsigned char zero_char[2]= {0, 0}; |
|
|
|
while(*num_data > 0 && aaip->aa_missing > 0) { |
|
if(aaip->rec_head_missing > 0) { |
|
aaip_consume_rec_head(aaip, data, num_data, 0); |
|
if(*num_data == 0 || aaip->aa_missing <= 0) |
|
return(1); |
|
} |
|
aaip_consume_rec_data(aaip, data, num_data, 0); |
|
} |
|
if(aaip->aa_missing <= 0) { |
|
if(aaip->aa_ends) { |
|
/* Check for incomplete pair and eventually make emergency closure */ |
|
if(aaip->rec_head_missing != 2) { /* incomplete record detected */ |
|
if(aaip->rec_head_missing) { |
|
/* fake 0 length record */ |
|
aaip_set_buffer_byte(aaip, aaip->recs_fill - 1, (unsigned char) 0, 0); |
|
aaip_push_to_recs(aaip, zero_char, 1, 0); |
|
} else { |
|
/* fill in missing btes */ |
|
for(i= 0; (int) i < aaip->rec_missing; i++) |
|
aaip_push_to_recs(aaip, zero_char, 1, 1); |
|
} |
|
aaip->rec_head_missing= 2; |
|
aaip->rec_missing= 0; |
|
aaip->num_recs++; |
|
if(aaip->rec_ends) { |
|
aaip->num_components++; |
|
aaip->end_of_components= aaip->recs_fill; |
|
} |
|
} |
|
if(aaip->end_of_components != aaip->recs_fill && |
|
aaip->end_of_components != 0) { |
|
/* incomplete component detected */ |
|
/* add empty end record */ |
|
aaip_push_to_recs(aaip, zero_char, 2, 0); |
|
aaip->num_recs++; |
|
aaip->num_components++; |
|
aaip->end_of_components= aaip->recs_fill; |
|
} |
|
if(!(aaip->first_is_name ^ (aaip->num_components % 2))) { |
|
/* value component is missing */ |
|
/* add dummy component */ |
|
aaip_push_to_recs(aaip, zero_char, 2, 0); |
|
aaip->num_recs++; |
|
aaip->num_components++; |
|
aaip->end_of_components= aaip->recs_fill; |
|
} |
|
aaip->aa_ends= 2; |
|
} else |
|
aaip->aa_head_missing= 5; |
|
} |
|
return(0); |
|
} |
|
|
|
|
|
/* Submit small data chunk for decoding. |
|
The return value will tell whether data are pending for being fetched. |
|
@param aaip The AAIP decoder context |
|
@param data Not more than 2048 bytes input for the decoder |
|
@parm num_data Number of bytes in data |
|
0 inquires the buffer status avoiding replies <= 0 |
|
@param ready_bytes Number of decoded bytes ready for delivery |
|
@param flag Bitfield for control purposes |
|
@return -1= non-AAIP field detected |
|
*ready_bytes gives number of consumed bytes in data |
|
0= cannot accept data because buffer full |
|
1= no component record complete, submit more data |
|
2= component record complete, may be delivered |
|
3= component complete, may be delivered |
|
4= no component available, no more data expected, done |
|
*/ |
|
int aaip_submit_data(struct aaip_state *aaip, |
|
unsigned char *data, size_t num_data, |
|
size_t *ready_bytes, int flag) |
|
{ |
|
int ret; |
|
unsigned char *in_data; |
|
|
|
if(aaip->aa_ends == 3) |
|
return(4); |
|
in_data= data; |
|
if(num_data == 0) |
|
goto ex; |
|
if(aaip->recs_fill + num_data > Aaip_buffer_sizE) |
|
return(0); |
|
|
|
while(num_data > 0) { |
|
if(aaip->aa_head_missing > 0) { |
|
ret= aaip_consume_aa_head(aaip, &data, &num_data, 0); |
|
if(ret < 0) { |
|
*ready_bytes= data - in_data; |
|
return(-1); |
|
} |
|
if(num_data == 0 || aaip->aa_missing <= 0) |
|
goto ex; |
|
} |
|
aaip_consume_aa_data(aaip, &data, &num_data, 0); |
|
if(aaip->aa_missing) |
|
break; |
|
} |
|
ex:; |
|
*ready_bytes= aaip->ready_bytes; |
|
if(aaip->num_components > 0) |
|
return(3); |
|
if(aaip->num_recs > 0) |
|
return(2); |
|
if(aaip->aa_ends && aaip->aa_head_missing == 0 && aaip->aa_missing == 0) |
|
aaip->aa_ends= 2; |
|
if(aaip->aa_ends == 2 && aaip->num_recs == 0) |
|
aaip->aa_ends= 3; |
|
if(aaip->aa_ends == 3) |
|
return(4); |
|
return(1); |
|
} |
|
|
|
|
|
/* Fetch the available part of current component. |
|
The return value will tell whether it belongs to name or to value and |
|
whether that name or value is completed now. |
|
@param aaip The AAIP decoder context |
|
@param result Has to point to storage for the component data |
|
@param result_size Gives the amount of provided result storage |
|
@param num_result Will tell the number of fetched result bytes |
|
@param flag Bitfield for control purposes |
|
bit0= discard data rather than copying to result |
|
@return -2 = insufficient result_size |
|
-1 = no data ready for delivery |
|
0 = result holds the final part of a name |
|
1 = result holds an intermediate part of a name |
|
2 = result holds the final part of a value |
|
3 = result holds an intermediate part of a value |
|
*/ |
|
int aaip_fetch_data(struct aaip_state *aaip, |
|
char *result, size_t result_size, size_t *num_result, |
|
int flag) |
|
{ |
|
int ret= -1, complete= 0, payload; |
|
unsigned int i, num_bytes= 0, h; |
|
|
|
if(aaip->num_recs == 0) |
|
return(-1); |
|
|
|
/* Copy data until end of buffer or end of component */ |
|
h= 0; |
|
for(i= 0; i < aaip->num_recs && !complete; i++) { |
|
payload= aaip_get_buffer_byte(aaip, h + 1, 0); |
|
if(!(flag & 1)) { |
|
if(num_bytes + payload > result_size) |
|
return(-2); |
|
aaip_read_from_recs(aaip, h + 2, (unsigned char *) (result + num_bytes), |
|
payload, 0); |
|
*num_result= num_bytes + payload; |
|
} |
|
num_bytes+= payload; |
|
if(!(aaip_get_buffer_byte(aaip, h, 0) & 1)) |
|
complete= 1; |
|
h+= payload + 2; |
|
} |
|
aaip->ready_bytes-= num_bytes; |
|
aaip->num_recs-= i; |
|
|
|
/* Shift buffer */ |
|
aaip_shift_recs(aaip, h, 0); |
|
|
|
/* Compute reply */ |
|
ret= 2 * !aaip->first_is_name; |
|
if(complete) { |
|
aaip->first_is_name= !aaip->first_is_name; |
|
if(aaip->num_components > 0) |
|
aaip->num_components--; |
|
} else |
|
ret|= 1; |
|
|
|
return(ret); |
|
} |
|
|
|
|
|
/* Skip the current component and eventually the following value component. |
|
This has to be called if fetching of a component shall be aborted |
|
but the next component resp. pair shall be fetchable again. |
|
aaip_submit_data() will not indicate readiness for fetching until all |
|
bytes of the skipped components are submitted. Those bytes get discarded. |
|
@param aaip The AAIP decoder context |
|
@param flag Bitfield for control purposes |
|
bit0= do not skip value if current component is name |
|
@return <=0 error , 1= now in skip state, 2= not in skip state |
|
*/ |
|
int aaip_skip_component(struct aaip_state *aaip, int flag) |
|
{ |
|
int to_skip= 1; |
|
|
|
if(aaip->first_is_name && !(flag & 1)) |
|
to_skip= 2; |
|
if(aaip->recs_invalid) { |
|
aaip->recs_invalid+= to_skip; |
|
return(1); |
|
} |
|
|
|
if(aaip->num_components) { |
|
/* null-fetch */ |
|
aaip_fetch_data(aaip, NULL, (size_t) 0, NULL, 1); |
|
to_skip--; |
|
} |
|
if(aaip->num_components && to_skip) { |
|
/* null-fetch */ |
|
aaip_fetch_data(aaip, NULL, (size_t) 0, NULL, 1); |
|
to_skip--; |
|
} |
|
if(to_skip) { |
|
aaip->recs_fill= 0; |
|
aaip->num_recs= 0; |
|
aaip->ready_bytes= 0; |
|
} |
|
aaip->recs_invalid= to_skip; |
|
if(aaip->aa_ends == 2 && aaip->num_recs == 0) |
|
aaip->aa_ends= 3; |
|
return(1 + (aaip->num_recs > 0)); |
|
} |
|
|
|
|
|
/* ------------------------- Pair Level Interface ------------------------ */ |
|
|
|
/* |
|
@param flag Bitfield for control purposes |
|
bit0= do not skip oversized component but return -2 |
|
@return see aaip_decode_pair |
|
*/ |
|
static int aaip_advance_pair(struct aaip_state *aaip, |
|
char *name, size_t name_size, size_t *name_fill, |
|
char *value, size_t value_size, size_t *value_fill, |
|
int flag) |
|
{ |
|
int ret; |
|
char *wpt; |
|
size_t size, num; |
|
|
|
retry:; |
|
if(aaip->first_is_name) { |
|
wpt= name + *name_fill; |
|
size= name_size - *name_fill; |
|
} else { |
|
wpt= value + *value_fill; |
|
size= value_size - *value_fill; |
|
} |
|
ret= aaip_fetch_data(aaip, wpt, size, &num, 0); |
|
if(ret == -2) { /* insufficient result size */ |
|
if(flag & 1) |
|
return(-2); |
|
ret= aaip_skip_component(aaip, 0); |
|
*name_fill= *value_fill= 0; |
|
aaip->pairs_skipped++; |
|
if(ret == 2) /* Skip performed, valid data pending */ |
|
goto retry; |
|
} else if(ret == -1) { /* No data ready for delivery : may not happen */ |
|
return(-1); |
|
} else if(ret == 0) { /* result holds the final part of a name */ |
|
(*name_fill)+= num; |
|
/* peek for value data */ |
|
ret= aaip_submit_data(aaip, NULL, (size_t) 0, &num, 0); |
|
if(ret == 2 || ret == 3) { |
|
/* fetch value data */; |
|
ret= aaip_advance_pair(aaip, name, name_size, name_fill, |
|
value, value_size, value_fill, flag); |
|
return ret; |
|
} else if(ret == 4) |
|
return(5); |
|
} else if(ret == 1) { /* result holds an intermediate part of a name */ |
|
(*name_fill)+= num; |
|
} else if(ret == 2) { /* result holds the final part of a value */ |
|
(*value_fill)+= num; |
|
if(aaip->num_components >= 2) |
|
return(3); |
|
if(aaip->aa_ends == 2 && aaip->num_recs == 0) |
|
aaip->aa_ends= 3; |
|
if(aaip->aa_ends == 3) |
|
return(4); |
|
return(2); |
|
} else if(ret == 3) { |
|
/* result holds an intermediate part of a value */; |
|
(*value_fill)+= num; |
|
} else { |
|
return(-1); /* unknown reply from aaip_fetch_data() */ |
|
} |
|
return(1); |
|
} |
|
|
|
|
|
/* Accept raw input data and collect a pair of name and value. |
|
The return value will indicate whether the pair is complete, whether more |
|
pairs are complete or whether more data are desired. No input data will be |
|
accepted as long as complete pairs are pending. The end of the attribute |
|
list will be indicated. |
|
@param aaip The AAIP decoder context |
|
@param data The raw data to decode |
|
@param num_data Number of data bytes provided |
|
@param consumed Returns the number of consumed data bytes |
|
@param name Buffer to build the name string |
|
@param name_size Maximum number of bytes in name |
|
@param name_fill Holds the current buffer fill of name |
|
@param value Buffer to build the value string |
|
@param value_size Maximum number of bytes in value |
|
@param value_fill Holds the current buffer fill of value |
|
@param flag Bitfield for control purposes |
|
bit0= do not skip oversized pair but return -2 |
|
@return <0 error |
|
-3 buffer full (program error) |
|
-2 insufficient result_size (only with flag bit0) |
|
-1 non-AAIP field detected |
|
0 data not accepted, first fetch pending pairs with num_data == 0 |
|
1 name and value are not valid yet, submit more data |
|
2 name and value are valid, submit more data |
|
3 name and value are valid, pairs pending, fetch with num_data == 0 |
|
4 name and value are valid, no more data expected |
|
5 name and value are not valid, no more data expected |
|
*/ |
|
int aaip_decode_pair(struct aaip_state *aaip, |
|
unsigned char *data, size_t num_data, size_t *consumed, |
|
char *name, size_t name_size, size_t *name_fill, |
|
char *value, size_t value_size, size_t *value_fill, |
|
int flag) |
|
{ |
|
int ret; |
|
size_t ready_bytes= 0; |
|
|
|
#ifdef Aaip_with_short_namespaceS |
|
char prefix[Aaip_max_name_expansioN + 1]; |
|
size_t nl, pl; |
|
#endif |
|
|
|
*consumed= 0; |
|
if((aaip->pair_status < 0 && aaip->pair_status != -2) || |
|
aaip->pair_status == 4 || |
|
aaip->pair_status == 5) { /* dead ends */ |
|
ret= aaip->pair_status; |
|
goto ex; |
|
} else if(aaip->pair_status == 2 || aaip->pair_status == 3) { |
|
if(aaip->pair_status == 3 && num_data > 0) |
|
{ret= 0; goto ex;} |
|
/* Start a new pair */ |
|
if(!aaip->first_is_name) /* Eventually skip orphaned value */ |
|
aaip_fetch_data(aaip, NULL, (size_t) 0, NULL, 1); |
|
*name_fill= *value_fill= 0; |
|
} |
|
|
|
if(num_data > 0) { |
|
ret= aaip_submit_data(aaip, data, num_data, &ready_bytes, 0); |
|
} else { |
|
ret= 1; |
|
if(aaip->num_components) |
|
ret= 3; |
|
else if(aaip->num_recs) |
|
ret= 2; |
|
} |
|
if(ret < 0) { /* non-AAIP field detected */ |
|
*consumed= ready_bytes; |
|
{ret= -1; goto ex;} |
|
} else if(ret == 0) { /* buffer overflow */; |
|
/* should not happen with correct usage */ |
|
{ret= -3; goto ex;} |
|
} else if(ret == 1) { /* no component record complete */ |
|
goto ex; |
|
} else if(ret == 2) { /* component record complete, may be delivered */ |
|
; |
|
} else if(ret == 3) { /* component complete, may be delivered */ |
|
; |
|
} else if(ret == 4) { /* no component available, no more data expected */ |
|
{ret= 5; goto ex;} |
|
} else |
|
{ret= -1; goto ex;} /* unknown reply from aaip_submit_data() */ |
|
|
|
*consumed= num_data; |
|
ret= aaip_advance_pair(aaip, name, name_size - Aaip_max_name_expansioN, |
|
name_fill, value, value_size, value_fill, flag & 1); |
|
if(aaip->aa_ends == 3) { |
|
if(ret >= 2 && ret <= 4) |
|
ret= 4; |
|
else |
|
ret= 5; |
|
} |
|
ex:; |
|
|
|
#ifdef Aaip_with_short_namespaceS |
|
|
|
if(ret >= 2 && ret <= 4 && *name_fill > 0) { |
|
/* Translate name from eventual short form */ |
|
nl= *name_fill; |
|
if(name[0] > 0 && name[0] <= Aaip_maxdef_namespacE) { |
|
prefix[0]= 0; |
|
if(name[0] == Aaip_namespace_literaL) { |
|
if(nl > 1) { |
|
/* Remove first character of name */ |
|
memmove(name, name + 1, nl - 1); |
|
(*name_fill)--; |
|
} |
|
} else if(name[0] == Aaip_namespace_systeM || |
|
name[0] == Aaip_namespace_useR || |
|
name[0] == Aaip_namespace_isofS || |
|
name[0] == Aaip_namespace_trusteD || |
|
name[0] == Aaip_namespace_securitY |
|
) { |
|
strcpy(prefix, Aaip_namespace_textS[(int) name[0]]); |
|
pl= strlen(prefix); |
|
memmove(name + pl, name + 1, nl - 1); |
|
memcpy(name, prefix, pl); |
|
*name_fill= pl + nl - 1; |
|
} |
|
} |
|
} |
|
|
|
#endif /* Aaip_with_short_namespaceS */ |
|
|
|
aaip->pair_status= ret; |
|
return(ret); |
|
} |
|
|
|
|
|
unsigned int aaip_get_pairs_skipped(struct aaip_state *aaip, int flag) |
|
{ |
|
return(aaip->pairs_skipped); |
|
} |
|
|
|
|
|
/* ------------------------- List Level Interface ------------------------ */ |
|
|
|
|
|
#define Aaip_initial_name_leN 256 |
|
#define Aaip_initial_value_leN 256 |
|
#define Aaip_initial_list_sizE 2 |
|
#define Aaip_list_enlargeR 1.5 |
|
|
|
|
|
/* |
|
@param flag Bitfield for control purposes |
|
bit0= do not update *buf_size |
|
*/ |
|
static int aaip_enlarge_buf(struct aaip_state *aaip, size_t memory_limit, |
|
size_t item_size, char **buf, size_t *buf_size, int flag) |
|
{ |
|
size_t new_size; |
|
char *new_buf; |
|
|
|
new_size= *buf_size * Aaip_list_enlargeR; |
|
if(aaip->list_mem_used + (new_size - *buf_size) * item_size >= memory_limit) |
|
return(3); |
|
aaip->list_mem_used+= (new_size - *buf_size) * item_size; |
|
new_buf= realloc(*buf, new_size * item_size); |
|
if(new_buf == NULL) |
|
return(-1); |
|
*buf= new_buf; |
|
if(!(flag & 1)) |
|
*buf_size= new_size; |
|
return(1); |
|
} |
|
|
|
|
|
/* Accept raw input data and collect arrays of name pointers, value lengths |
|
and value pointers. A handle object will emerge which finally has to be |
|
be freed by a call with bit 15. |
|
@param handle The decoding context. |
|
It will be created by this call with flag bit 0 or if |
|
*handle == NULL. This handle has to be the same as long |
|
as decoding goes on and finally has to be freed by a |
|
call with bit15. |
|
@param memory_limit Maximum number of bytes to allocate |
|
@param num_attr_limit Maximum number of name-value pairs to allocate |
|
@param data The raw data to decode |
|
@param num_data Number of data bytes provided |
|
@param consumed Returns the number of consumed data bytes |
|
@param flag Bitfield for control purposes |
|
bit0= this is the first call with the given handle |
|
(also in effect if *handle is NULL) |
|
bit15= end decoding : |
|
Free handle and its intermediate list memory. |
|
@return <=0 error |
|
-4 interpretation stalled, no valid result |
|
-3 program error, unexpected reply from lower layers |
|
-2 non-AAIP-field detected, arrays are complete, |
|
call aaip_get_decoded_attrs() |
|
-1 out of memory |
|
1 not complete yet, submit more data |
|
2 arrays are complete, call aaip_get_decoded_attrs() |
|
3 limit exceeded, not complete yet, |
|
enlarge memory_limit or call with bit15 and give up |
|
4 limit exceeded, call aaip_get_decoded_attrs() and try again |
|
*/ |
|
int aaip_decode_attrs(struct aaip_state **handle, |
|
size_t memory_limit, size_t num_attr_limit, |
|
unsigned char *data, size_t num_data, size_t *consumed, |
|
int flag) |
|
{ |
|
int ret; |
|
struct aaip_state *aaip; |
|
size_t h_num, *h_lengths, i, new_mem, pair_consumed= 0; |
|
char **h_names, **h_values, *hpt; |
|
|
|
*consumed= 0; |
|
if(flag & (1 << 15)) { |
|
if(*handle == NULL) |
|
return(0); |
|
ret= aaip_get_decoded_attrs(handle, &h_num, &h_names, &h_lengths, &h_values, |
|
0); |
|
if(ret > 0) |
|
aaip_get_decoded_attrs(handle, &h_num, &h_names, &h_lengths, &h_values, |
|
1 << 15); |
|
if((*handle)->name_buf != NULL) |
|
free((*handle)->name_buf); |
|
if((*handle)->value_buf != NULL) |
|
free((*handle)->value_buf); |
|
free((char *) *handle); |
|
*handle= NULL; |
|
return(1); |
|
} |
|
|
|
aaip= *handle; |
|
if(aaip == NULL || (flag & 1)) { |
|
aaip= *handle= calloc(1, sizeof(struct aaip_state)); |
|
if(*handle == NULL) |
|
return(-1); |
|
aaip_init_aaip_state(*handle, 0); |
|
} |
|
if(aaip->list_names == NULL || aaip->list_values == NULL || |
|
aaip->list_value_lengths == NULL) { |
|
/* Initialize arrays */ |
|
aaip->list_size= Aaip_initial_list_sizE; |
|
if(num_attr_limit > 0 && num_attr_limit < aaip->list_size) |
|
aaip->list_size= num_attr_limit; |
|
new_mem= aaip->list_size * (2*sizeof(char *) + sizeof(size_t)) + |
|
Aaip_initial_name_leN + Aaip_initial_value_leN; |
|
if(aaip->list_mem_used + new_mem >= memory_limit) |
|
return(3); |
|
aaip->list_mem_used+= new_mem; |
|
aaip->list_names= calloc(sizeof(char *), aaip->list_size); |
|
aaip->list_value_lengths= calloc(sizeof(size_t), aaip->list_size); |
|
aaip->list_values= calloc(sizeof(char *), aaip->list_size); |
|
if(aaip->list_names == NULL || aaip->list_value_lengths == NULL || |
|
aaip->list_values == NULL) |
|
return(-1); |
|
for(i= 0; i < aaip->list_size; i++) { |
|
aaip->list_names[i]= NULL; |
|
aaip->list_value_lengths[i]= 0; |
|
aaip->list_values[i]= NULL; |
|
} |
|
} |
|
if(aaip->name_buf == NULL || aaip->value_buf == NULL) { |
|
new_mem= Aaip_initial_name_leN + Aaip_initial_value_leN; |
|
if(aaip->list_mem_used >= memory_limit) |
|
return(3); |
|
aaip->list_mem_used+= new_mem; |
|
aaip->name_buf= calloc(1, Aaip_initial_name_leN); |
|
aaip->value_buf= calloc(1, Aaip_initial_value_leN); |
|
if(aaip->name_buf == NULL || aaip->value_buf == NULL) |
|
return(-1); |
|
aaip->name_buf_size= Aaip_initial_name_leN; |
|
aaip->value_buf_size= Aaip_initial_name_leN; |
|
} |
|
|
|
while(1) { |
|
if(aaip->list_pending_pair > 0) { |
|
/* the buffer holds a complete pair from a previous memory limit refusal */ |
|
ret= aaip->list_pending_pair; |
|
aaip->list_pending_pair= 0; |
|
} else { |
|
ret= aaip_decode_pair(aaip, data, num_data, &pair_consumed, |
|
aaip->name_buf, aaip->name_buf_size, &aaip->name_buf_fill, |
|
aaip->value_buf, aaip->value_buf_size, &aaip->value_buf_fill, |
|
1); |
|
*consumed+= pair_consumed; |
|
} |
|
if(ret == -2) { /* insufficient result_size */ |
|
if(aaip->first_is_name) |
|
ret= aaip_enlarge_buf(aaip, memory_limit, (size_t) 1, &(aaip->name_buf), |
|
&(aaip->name_buf_size), 0); |
|
else |
|
ret= aaip_enlarge_buf(aaip, memory_limit, (size_t) 1, |
|
&(aaip->value_buf), &(aaip->value_buf_size), 0); |
|
if(ret != 1) |
|
return(ret); |
|
|
|
} else if(ret == -1) { /* non-AAIP field detected */ |
|
if(pair_consumed <= 0) |
|
return(-4); /* interpretation did not advance */ |
|
|
|
} else if(ret < 0) { /* other error */ |
|
return(-3); |
|
|
|
} else if(ret == 0) { /* first fetch pending pairs with num_data == 0 */ |
|
/* should not happen, fetch more pairs */; |
|
|
|
} else if(ret == 1) { |
|
/* name and value are not valid yet, submit more data */ |
|
return(1); |
|
|
|
} else if(ret == 2 || ret == 3 || ret == 4) { |
|
/* name and value are valid, submit more data */ |
|
/* name and value are valid, pairs pending, fetch with num_data == 0 */ |
|
/* name and value are valid, no more data expected */ |
|
aaip->list_pending_pair= ret; |
|
|
|
if(aaip->list_num_attrs >= aaip->list_size) { |
|
hpt= (char *) aaip->list_names; |
|
ret= aaip_enlarge_buf(aaip, memory_limit, sizeof(char *), |
|
&hpt, &(aaip->list_size), 1); |
|
if(ret != 1) |
|
return(ret); |
|
aaip->list_names= (char **) hpt; |
|
hpt= (char *) aaip->list_values; |
|
ret= aaip_enlarge_buf(aaip, memory_limit, sizeof(char *), |
|
&hpt, &(aaip->list_size), 1); |
|
if(ret != 1) |
|
return(ret); |
|
aaip->list_values= (char **) hpt; |
|
hpt= (char *) aaip->list_value_lengths; |
|
ret= aaip_enlarge_buf(aaip, memory_limit, sizeof(size_t), |
|
&hpt, &(aaip->list_size), 0); |
|
if(ret != 1) |
|
return(ret); |
|
aaip->list_value_lengths= (size_t *) hpt; |
|
} |
|
|
|
/* Allocate name and value in list */; |
|
if(aaip->list_mem_used + aaip->name_buf_fill + aaip->value_buf_fill + 2 |
|
> memory_limit) { |
|
return(3); |
|
} |
|
aaip->list_mem_used+= aaip->name_buf_fill + aaip->value_buf_fill + 2; |
|
i= aaip->list_num_attrs; |
|
aaip->list_names[i]= calloc(aaip->name_buf_fill + 1, 1); |
|
aaip->list_values[i]= calloc(aaip->value_buf_fill + 1, 1); |
|
memcpy(aaip->list_names[i], aaip->name_buf, aaip->name_buf_fill); |
|
aaip->list_names[i][aaip->name_buf_fill]= 0; |
|
memcpy(aaip->list_values[i], aaip->value_buf, aaip->value_buf_fill); |
|
aaip->list_values[i][aaip->value_buf_fill]= 0; |
|
aaip->list_value_lengths[i]= aaip->value_buf_fill; |
|
aaip->list_num_attrs++; |
|
aaip->name_buf_fill= aaip->value_buf_fill= 0; |
|
|
|
ret= aaip->list_pending_pair; |
|
aaip->list_pending_pair= 0; |
|
|
|
if(ret == 2) |
|
return(1); |
|
if(ret == 4) |
|
break; |
|
|
|
} else if(ret == 5) |
|
break; |
|
else |
|
return(-2); |
|
|
|
num_data= 0; /* consume pending pairs */ |
|
} |
|
aaip->list_pending_pair= 5; |
|
return(2); |
|
} |
|
|
|
|
|
/* Obtain the resulting attributes when aaip_decode_attrs() indicates to |
|
be done or to have the maximum possible amount of result ready. |
|
The returned data objects finally have to be freed by a call with flag |
|
bit 15. |
|
@param handle The decoding context created by aaip_decode_attrs() |
|
@param num_attrs Will return the number of name-value pairs |
|
@param names Will return an array of pointers to 0-terminated names |
|
@param value_lengths Will return an arry with the lenghts of values |
|
@param values Will return an array of pointers to 8-bit values |
|
@param flag Bitfield for control purposes |
|
bit15= free memory of names, value_lengths, values |
|
@return <0 error |
|
0 no attribute list ready |
|
1 ok |
|
*/ |
|
int aaip_get_decoded_attrs(struct aaip_state **handle, size_t *num_attrs, |
|
char ***names, size_t **value_lengths, char ***values, |
|
int flag) |
|
{ |
|
size_t i; |
|
struct aaip_state *aaip; |
|
|
|
aaip= *((struct aaip_state **) handle); |
|
if(flag & (1 << 15)) { |
|
if(*names != NULL) { |
|
for(i= 0; i < *num_attrs; i++) { |
|
if((*names)[i] != NULL) |
|
free((*names)[i]); |
|
(*names)[i]= NULL; |
|
} |
|
free(*names); |
|
*names= NULL; |
|
} |
|
if(*values != NULL) { |
|
for(i= 0; i < *num_attrs; i++) { |
|
if((*values)[i] != NULL) |
|
free((*values)[i]); |
|
(*values)[i]= NULL; |
|
} |
|
free(*values); |
|
*values= NULL; |
|
} |
|
if(*value_lengths != NULL) |
|
free(*value_lengths); |
|
*value_lengths= NULL; |
|
*num_attrs= 0; |
|
return(1); |
|
} |
|
|
|
/* Check whether decoding is finished yet */ |
|
if(aaip->list_pending_pair != 5) |
|
return(0); |
|
|
|
*num_attrs= aaip->list_num_attrs; |
|
*names= aaip->list_names; |
|
*value_lengths= aaip->list_value_lengths; |
|
*values= aaip->list_values; |
|
|
|
/* Now the memory is owned by the caller */ |
|
aaip->list_num_attrs= 0; |
|
aaip->list_names= NULL; |
|
aaip->list_value_lengths= NULL; |
|
aaip->list_values= NULL; |
|
aaip->list_size= 0; |
|
aaip->list_pending_pair= 0; |
|
return(1); |
|
} |
|
|
|
|
|
/* ------ Decoder for ACLs ------ */ |
|
|
|
|
|
static int aaip_write_acl_line(char **result, size_t *result_size, |
|
char *tag_type, char *qualifier, |
|
char *permissions, int flag) |
|
{ |
|
size_t needed, tag_len, perm_len, qualifier_len; |
|
|
|
tag_len= strlen(tag_type); |
|
qualifier_len= strlen(qualifier); |
|
perm_len= strlen(permissions); |
|
needed= tag_len + qualifier_len + perm_len + 3; |
|
if((flag & 1)) { |
|
(*result_size)+= needed; |
|
return(1); |
|
} |
|
if(needed + 1 > *result_size) /* +1 : want to append a trailing 0 */ |
|
return(-1); |
|
memcpy((*result), tag_type, tag_len); |
|
(*result)[tag_len]= ':'; |
|
memcpy((*result) + tag_len + 1, qualifier, qualifier_len); |
|
(*result)[tag_len + 1 + qualifier_len]= ':'; |
|
memcpy((*result) + tag_len + 1 + qualifier_len + 1, permissions, perm_len); |
|
(*result)[tag_len + 1 + qualifier_len + 1 + perm_len]= '\n'; |
|
(*result)[tag_len + 1 + qualifier_len + 1 + perm_len + 1] = 0; |
|
(*result)+= needed; |
|
(*result_size)-= needed; |
|
return(1); |
|
} |
|
|
|
|
|
static int aaip_read_qualifier(unsigned char *data, size_t num_data, |
|
char *name, size_t name_size, size_t *name_fill, |
|
int flag) |
|
{ |
|
int is_done= 0; |
|
size_t rec_len= 0; |
|
unsigned char *rpt; |
|
|
|
*name_fill= 0; |
|
for(rpt= data; !is_done; rpt+= rec_len) { |
|
rec_len= (*rpt) & 127; |
|
is_done= !((*rpt) & 128); |
|
if(*name_fill + rec_len >= name_size || |
|
(size_t) (rpt + 1 + rec_len - data) > num_data) |
|
return(-1); |
|
memcpy(name + *name_fill, rpt + 1, rec_len); |
|
rpt+= 1 + rec_len; |
|
(*name_fill)+= rec_len; |
|
name[*name_fill]= 0; |
|
} |
|
return(1); |
|
} |
|
|
|
|
|
/* Convert an AAIP ACL attribute value into the long text form of ACL. |
|
@param data The raw data to decode |
|
@param num_data Number of data bytes provided |
|
@param consumed Returns the number of consumed data bytes |
|
@param acl_text Will be filled with ACL long text form |
|
@param acl_text_size Maximum number of bytes to be written to acl_text |
|
@param acl_text_fill Will return the number of bytes in acl_text |
|
@param flag Bitfield for control purposes |
|
bit0= count only, do not really produce bytes: |
|
acl_text will not be touched, |
|
acl_text_size will be ignored, |
|
*acl_text_fill will return the counted number |
|
plus 1 for a trailing zero. |
|
bit1= expected is a default ACL (see return value 2) |
|
@return 1 success |
|
2 success, begin of default/access ACL encountered, |
|
submit data + *consumed for access/default ACL |
|
-1 error with reading of qualifier |
|
-2 error with writing of ACL text line |
|
-3 version mismatch |
|
-4 unknown tag type encountered |
|
*/ |
|
int aaip_decode_acl(unsigned char *data, size_t num_data, size_t *consumed, |
|
char *acl_text, size_t acl_text_size, |
|
size_t *acl_text_fill, int flag) |
|
{ |
|
unsigned char *rpt; |
|
char perm_text[4], *wpt, *name= NULL; |
|
int type, qualifier= 0, perm, ret, cnt, name_size= 1024; |
|
size_t w_size= 0, name_fill= 0, i; |
|
uid_t uid; |
|
gid_t gid; |
|
struct passwd *pwd; |
|
struct group *grp; |
|
|
|
LIBISO_ALLOC_MEM(name, char, name_size); |
|
cnt= flag & 1; |
|
*consumed= 0; |
|
wpt= acl_text; |
|
w_size= acl_text_size; |
|
*acl_text_fill= 0; |
|
for(rpt= data; (size_t) (rpt - data) < num_data; ) { |
|
perm= *rpt; |
|
strcpy(perm_text, "---"); |
|
if(perm & Aaip_READ) |
|
perm_text[0]= 'r'; |
|
if(perm & Aaip_WRITE) |
|
perm_text[1]= 'w'; |
|
if(perm & Aaip_EXEC) |
|
perm_text[2]= 'x'; |
|
|
|
type= (*rpt) >> 4; |
|
if(type == Aaip_FUTURE_VERSION) /* indicate to caller: version mismatch */ |
|
{ret = -3; goto ex;} |
|
|
|
qualifier= !!((*rpt) & 8); |
|
if(qualifier) { |
|
ret= aaip_read_qualifier(rpt + 1, num_data - (rpt + 1 - data), |
|
name, name_size, &name_fill, 0); |
|
if(ret <= 0) |
|
{ret = -1; goto ex;} |
|
} |
|
|
|
/* Advance read pointer */ |
|
(*consumed)+= 1 + (qualifier ? name_fill + 1 : 0); |
|
rpt+= 1 + (qualifier ? name_fill + 1 : 0); |
|
|
|
ret= 1; |
|
if(type == Aaip_TRANSLATE) { |
|
/* rightfully ignored yet */; |
|
continue; |
|
} else if(type == Aaip_ACL_USER_OBJ) { |
|
/* user::rwx */ |
|
ret= aaip_write_acl_line(&wpt, &w_size, "user", "", perm_text, cnt); |
|
} else if(type == Aaip_ACL_USER) { |
|
/* user:<username>:rwx */; |
|
ret= aaip_write_acl_line(&wpt, &w_size, "user", name, perm_text, cnt); |
|
} else if(type == Aaip_ACL_GROUP_OBJ) { |
|
/* user::rwx */ |
|
ret= aaip_write_acl_line(&wpt, &w_size, "group", "", perm_text, cnt); |
|
} else if(type == Aaip_ACL_GROUP) { |
|
/* group:<groupname>:rwx */; |
|
ret= aaip_write_acl_line(&wpt, &w_size, "group", name, perm_text, cnt); |
|
} else if(type == Aaip_ACL_MASK) { |
|
/* mask::rwx */ |
|
ret= aaip_write_acl_line(&wpt, &w_size, "mask", "", perm_text, cnt); |
|
} else if(type == Aaip_ACL_OTHER) { |
|
/* other::rwx */ |
|
ret= aaip_write_acl_line(&wpt, &w_size, "other", "", perm_text, cnt); |
|
} else if(type == Aaip_SWITCH_MARK) { |
|
/* Indicate to caller: end of desired ACL type access/default */ |
|
if((perm & Aaip_EXEC) ^ (!!(flag & 2))) |
|
{ret= 2; goto ex;} |
|
} else if(type == Aaip_ACL_USER_N) { |
|
/* determine username from uid */ |
|
uid= 0; |
|
for(i= 0; i < name_fill; i++) |
|
uid= (uid << 8) | ((unsigned char *) name)[i]; |
|
pwd= getpwuid(uid); |
|
if(pwd == NULL) |
|
sprintf(name, "%.f", (double) uid); |
|
else if(strlen(pwd->pw_name) >= (size_t) name_size) |
|
sprintf(name, "%.f", (double) uid); |
|
else |
|
strcpy(name, pwd->pw_name); |
|
/* user:<username>:rwx */; |
|
ret= aaip_write_acl_line(&wpt, &w_size, "user", name, perm_text, cnt); |
|
} else if(type == Aaip_ACL_GROUP_N) { |
|
/* determine username from gid */; |
|
gid= 0; |
|
for(i= 0; i < name_fill; i++) |
|
gid= (gid << 8) | ((unsigned char *) name)[i]; |
|
grp= getgrgid(gid); |
|
if(grp == NULL) |
|
sprintf(name, "%.f", (double) gid); |
|
else if(strlen(grp->gr_name) >= (size_t) name_size) |
|
sprintf(name, "%.f", (double) gid); |
|
else |
|
strcpy(name, grp->gr_name); |
|
/* user:<username>:rwx */; |
|
ret= aaip_write_acl_line(&wpt, &w_size, "group", name, perm_text, cnt); |
|
} else { |
|
/* indicate to caller: unknown type */ |
|
{ret = -4; goto ex;} |
|
} |
|
if(ret <= 0) |
|
{ret = -2; goto ex;} |
|
} |
|
ret= 1; |
|
ex:; |
|
*acl_text_fill= w_size; |
|
if(flag & 1) |
|
(*acl_text_fill)++; |
|
LIBISO_FREE_MEM(name); |
|
return(ret); |
|
} |
|
|
|
|
|
/* ----------------------- Adapter for operating systems ----------------- */ |
|
|
|
|
|
#ifdef Libisofs_use_os_dummY |
|
|
|
#include "aaip-os-dummy.c" |
|
|
|
#else |
|
#ifdef __FreeBSD__ |
|
|
|
#include "aaip-os-freebsd.c" |
|
|
|
#else |
|
#ifdef __FreeBSD_kernel__ |
|
|
|
#ifdef NIX |
|
#ifdef Libisofs_with_aaip_xattR |
|
/* ts B51213: xattr system library calls are only stubs */ |
|
#include "aaip-os-linux.c" |
|
#else |
|
/* ts B51213: extattr system library calls are not even present */ |
|
#include "aaip-os-freebsd.c" |
|
#endif /* ! Libisofs_with_aaip_xattR */ |
|
#else /* NIX */ |
|
/* ts B51213: so we still end up at the dummy */ |
|
#include "aaip-os-dummy.c" |
|
#endif /* ! NIX */ |
|
|
|
#else |
|
#ifdef __NetBSD__ |
|
|
|
#include "aaip-os-freebsd.c" |
|
|
|
#else |
|
#ifdef __OpenBSD__ |
|
|
|
#include "aaip-os-freebsd.c" |
|
|
|
#else |
|
#ifdef __linux |
|
|
|
#include "aaip-os-linux.c" |
|
|
|
/* August 2011: aaip-os-linux.c would also work for GNU/Hurd : ifdef __GNU__ |
|
Libraries and headers are present on Debian GNU/Hurd but there is no |
|
ACL or xattr support in the filesystems yet. |
|
Further, llistxattr() produces ENOSYS "Function not implemented". |
|
So it makes few sense to enable it here. |
|
*/ |
|
|
|
#else |
|
|
|
#include "aaip-os-dummy.c" |
|
|
|
#endif /* ! __linux */ |
|
#endif /* ! __OpenBSD__ */ |
|
#endif /* ! __NetBSD__ */ |
|
#endif /* ! __FreeBSD_kernel__ */ |
|
#endif /* ! __FreeBSD__ */ |
|
#endif /* ! Libisofs_use_os_dummY */ |
|
|
|
|