#include <stdio.h>
#include <stdlib.h>
#include <syslog.h>
#include <string.h>
#include <OpenDirectory/OpenDirectory.h>
#include "automount.h"
#include "automount_od.h"
typedef enum {
OD_CB_KEEPGOING,
OD_CB_DONE,
OD_CB_REJECTED,
OD_CB_ERROR
} callback_ret_t;
typedef callback_ret_t (*callback_fn)(CFStringRef key, CFStringRef value,
void *udata);
static callback_ret_t mastermap_callback(CFStringRef key, CFStringRef value,
void *udata);
static callback_ret_t directmap_callback(CFStringRef key, CFStringRef value,
void *udata);
static callback_ret_t match_callback(CFStringRef key, CFStringRef value,
void *udata);
static callback_ret_t readdir_callback(CFStringRef key, CFStringRef value,
void *udata);
struct match_cbdata {
const char *map;
const char *key;
char **od_line;
int *od_len;
};
struct loadmaster_cbdata {
char *defopts;
char **stack;
char ***stkptr;
};
struct loaddirect_cbdata {
char *opts;
char *localmap;
char **stack;
char ***stkptr;
};
struct dir_cbdata {
struct dir_entry **list;
struct dir_entry *last;
int error;
};
static int od_match(const char *map, const char *key, char **od_line,
int *od_len);
static int od_search(CFStringRef attr_to_match, char *value_to_match,
callback_fn callback, void *udata);
static inline CFIndex
od_cfstrlen(CFStringRef cfstr)
{
return (CFStringGetMaximumSizeForEncoding(CFStringGetLength(cfstr),
kCFStringEncodingUTF8));
}
static inline Boolean
od_cfstrlcpy(char *string, CFStringRef cfstr, size_t size)
{
return (CFStringGetCString(cfstr, string, (CFIndex)size,
kCFStringEncodingUTF8));
}
static char *
od_CFStringtoCString(CFStringRef cfstr)
{
char *string;
CFIndex length;
length = od_cfstrlen(cfstr);
string = malloc(length + 1);
if (string == NULL)
return (NULL);
if (!od_cfstrlcpy(string, cfstr, length + 1)) {
free(string);
return (NULL);
}
return (string);
}
char *
od_get_error_string(CFErrorRef err)
{
CFStringRef errstringref;
char *errstring;
if (err != NULL) {
errstringref = CFErrorCopyDescription(err);
errstring = od_CFStringtoCString(errstringref);
CFRelease(errstringref);
} else
errstring = strdup("Unknown error");
return (errstring);
}
void
init_od(__unused char **stack, __unused char ***stkptr)
{
}
int
getmapent_od(const char *key, const char *map, struct mapline *ml,
__unused char **stack, __unused char ***stkptr,
bool_t *iswildcard, __unused bool_t isrestricted)
{
char *od_line = NULL;
char *lp;
int od_len;
size_t len;
int nserr;
if (trace > 1)
trace_prt(1, "getmapent_od called\n");
if (trace > 1) {
trace_prt(1, "getmapent_od: key=[ %s ]\n", key);
}
if (iswildcard)
*iswildcard = FALSE;
nserr = od_match(map, key, &od_line, &od_len);
if (nserr) {
if (nserr == __NSW_NOTFOUND) {
if ((nserr = od_match(map, "*", &od_line,
&od_len)))
goto done;
else {
if (iswildcard)
*iswildcard = TRUE;
}
} else
goto done;
}
if ((lp = strchr(od_line, '#')) != NULL)
*lp = '\0';
len = strlen(od_line);
if (len == 0) {
nserr = __NSW_NOTFOUND;
goto done;
}
lp = &od_line[len - 1];
while (lp > od_line && isspace(*lp))
*lp-- = '\0';
if (lp == od_line) {
nserr = __NSW_NOTFOUND;
goto done;
}
(void) strncpy(ml->linebuf, od_line, LINESZ);
unquote(ml->linebuf, ml->lineqbuf);
nserr = __NSW_SUCCESS;
done:
if (od_line)
free((char *)od_line);
if (trace > 1)
trace_prt(1, "getmapent_od: exiting ...\n");
return (nserr);
}
static callback_ret_t
match_callback(CFStringRef key, CFStringRef value, void *udata)
{
char *key_str, *value_str;
CFIndex value_len;
struct match_cbdata *temp = (struct match_cbdata *)udata;
char **od_line = temp->od_line;
int *od_len = temp->od_len;
if (trace > 1) {
key_str = od_CFStringtoCString(key);
value_str = od_CFStringtoCString(value);
if (key_str != NULL && value_str != NULL) {
trace_prt(1, " match_callback called: key %s, value %s\n",
key_str, value_str);
}
free(value_str);
free(key_str);
}
value_len = od_cfstrlen(value);
*od_len = (int)(value_len + 1);
if (*od_len > LINESZ) {
key_str = od_CFStringtoCString(key);
pr_msg(
"Open Directory map %s, entry for %s"
" is too long %d chars (max %d)",
temp->map, key_str, *od_len, LINESZ);
free(key_str);
return (OD_CB_REJECTED);
}
*od_line = (char *)malloc(*od_len);
if (*od_line == NULL) {
pr_msg("match_callback: malloc failed");
return (OD_CB_ERROR);
}
if (!od_cfstrlcpy(*od_line, value, *od_len)) {
key_str = od_CFStringtoCString(key);
pr_msg("match_callback: can't get line for %s", key_str);
free(key_str);
free(*od_line);
return (OD_CB_ERROR);
}
if (trace > 1)
trace_prt(1, " match_callback: found: %s\n", *od_line);
return (OD_CB_DONE);
}
static int
od_match(const char *map, const char *key, char **od_line, int *od_len)
{
int ret;
char *pattern;
struct match_cbdata cbdata;
if (trace > 1) {
trace_prt(1, "od_match called\n");
trace_prt(1, "od_match: key =[ %s ]\n", key);
}
if (asprintf(&pattern, "%s,automountMapName=%s", key, map) == -1) {
pr_msg("od_match: malloc failed");
ret = __NSW_UNAVAIL;
goto done;
}
if (trace > 1)
trace_prt(1, " od_match: Searching for %s\n", pattern);
cbdata.map = map;
cbdata.key = key;
cbdata.od_line = od_line;
cbdata.od_len = od_len;
ret = od_search(kODAttributeTypeRecordName, pattern, match_callback,
(void *) &cbdata);
free(pattern);
if (trace > 1) {
if (ret == __NSW_NOTFOUND)
trace_prt(1, " od_match: no entries found\n");
else if (ret != __NSW_UNAVAIL)
trace_prt(1,
" od_match: od_search FAILED: %d\n", ret);
else
trace_prt(1, " od_match: od_search OK\n");
}
if (verbose) {
if (ret == __NSW_NOTFOUND)
pr_msg("od_search failed");
}
done:
return (ret);
}
int
loadmaster_od(char *mapname, char *defopts, char **stack, char ***stkptr)
{
int res;
struct loadmaster_cbdata master_cbdata;
if (trace > 1)
trace_prt(1, "loadmaster_od called\n");
master_cbdata.defopts = defopts;
master_cbdata.stack = stack;
master_cbdata.stkptr = stkptr;
if (trace > 1)
trace_prt(1, "loadmaster_od: Requesting list in %s\n",
mapname);
res = od_search(kODAttributeTypeMetaAutomountMap, mapname,
mastermap_callback, (void *) &master_cbdata);
if (trace > 1)
trace_prt(1,
"loadmaster_od: od_search just returned: %d\n",
res);
return (res);
}
int
loaddirect_od(char *nsmap, char *localmap, char *opts,
char **stack, char ***stkptr)
{
struct loaddirect_cbdata direct_cbdata;
if (trace > 1) {
trace_prt(1, "loaddirect_od called\n");
}
if (trace > 1)
trace_prt(1, "loaddirect_od: Requesting list for %s in %s\n",
localmap, nsmap);
direct_cbdata.opts = opts;
direct_cbdata.localmap = localmap;
direct_cbdata.stack = stack;
direct_cbdata.stkptr = stkptr;
return (od_search(kODAttributeTypeMetaAutomountMap, nsmap,
directmap_callback, (void *) &direct_cbdata));
}
static callback_ret_t
mastermap_callback(CFStringRef key, CFStringRef invalue, void *udata)
{
char *key_str, *value_str, *value;
CFIndex key_len, value_len;
char *pmap, *opts;
char dir[LINESZ], map[LINESZ], qbuff[LINESZ];
struct loadmaster_cbdata *temp = (struct loadmaster_cbdata *)udata;
char *defopts = temp->defopts;
char **stack = temp->stack;
char ***stkptr = temp->stkptr;
CFIndex i;
if (trace > 1) {
key_str = od_CFStringtoCString(key);
value_str = od_CFStringtoCString(invalue);
if (key_str != NULL && value_str != NULL) {
trace_prt(1, " mastermap_callback called: key %s, value %s\n",
key_str, value_str);
}
free(value_str);
free(key_str);
}
key_len = od_cfstrlen(key);
value_len = od_cfstrlen(invalue);
if (key_len >= LINESZ || value_len >= LINESZ)
return (OD_CB_KEEPGOING);
if (key_len < 2 || value_len < 2)
return (OD_CB_KEEPGOING);
value_str = od_CFStringtoCString(invalue);
value = value_str;
i = value_len;
while (i > 0 && isspace((unsigned char)*value)) {
value++;
i--;
}
if (*value == '\0') {
free(value_str);
return (OD_CB_KEEPGOING);
}
if (!od_cfstrlcpy(dir, key, key_len)) {
free(value_str);
return (OD_CB_KEEPGOING);
}
if (isspace((unsigned char)dir[0]) || dir[0] == '#') {
free(value_str);
return (OD_CB_KEEPGOING);
}
if (trace > 1)
trace_prt(1, "mastermap_callback: dir= [ %s ]\n", dir);
for (i = 0; i < LINESZ; i++)
qbuff[i] = ' ';
switch (macro_expand("", dir, qbuff, sizeof (dir))) {
case MEXPAND_OK:
break;
case MEXPAND_LINE_TOO_LONG:
pr_msg(
"%s in Open Directory map: entry too long (max %zu chars)",
dir, sizeof (dir) - 1);
free(value_str);
return (OD_CB_KEEPGOING);
case MEXPAND_VARNAME_TOO_LONG:
pr_msg(
"%s in Open Directory map: variable name too long",
dir);
free(value_str);
return (OD_CB_KEEPGOING);
}
strlcpy(map, value, sizeof (map));
free(value_str);
if (trace > 1)
trace_prt(1, "mastermap_callback: map= [ %s ]\n", map);
switch (macro_expand("", map, qbuff, sizeof (map))) {
case MEXPAND_OK:
break;
case MEXPAND_LINE_TOO_LONG:
pr_msg(
"%s in Open Directory map: entry too long (max %zu chars)",
map, sizeof (map) - 1);
return (OD_CB_KEEPGOING);
case MEXPAND_VARNAME_TOO_LONG:
pr_msg(
"%s in Open Directory map: variable name too long",
map);
return (OD_CB_KEEPGOING);
}
pmap = map;
while (*pmap && isspace(*pmap))
pmap++;
opts = pmap;
while (*opts && !isspace(*opts))
opts++;
if (*opts) {
*opts++ = '\0';
while (*opts && isspace(*opts))
opts++;
if (*opts == '-')
opts++;
else
opts = defopts;
}
if (strcspn(opts, " \t") == strlen(opts)) {
if (trace > 1)
trace_prt(1,
"mastermap_callback: dir=[ %s ], pmap=[ %s ]\n",
dir, pmap);
dirinit(dir, pmap, opts, 0, stack, stkptr);
} else {
pr_msg(
"Warning: invalid entry for %s in Open Directory ignored.\n",
dir);
}
if (trace > 1)
trace_prt(1, "mastermap_callback exiting...\n");
return (OD_CB_KEEPGOING);
}
static callback_ret_t
directmap_callback(CFStringRef key, __unused CFStringRef value, void *udata)
{
char *str;
CFIndex key_len;
char dir[MAXFILENAMELEN+1];
struct loaddirect_cbdata *temp = (struct loaddirect_cbdata *)udata;
char *opts = temp->opts;
char *localmap = temp->localmap;
char **stack = temp->stack;
char ***stkptr = temp->stkptr;
if (trace > 1) {
str = od_CFStringtoCString(key);
if (str != NULL) {
trace_prt(1, " directmap_callback called: key %s\n",
str);
free(str);
}
}
key_len = od_cfstrlen(key);
if (key_len > (CFIndex)MAXFILENAMELEN || key_len < 2)
return (OD_CB_KEEPGOING);
if (!od_cfstrlcpy(dir, key, key_len))
return (OD_CB_KEEPGOING);
if (isspace((unsigned char)dir[0]) || dir[0] == '#')
return (OD_CB_KEEPGOING);
dirinit(dir, localmap, opts, 1, stack, stkptr);
return (OD_CB_KEEPGOING);
}
int
getmapkeys_od(char *nsmap, struct dir_entry **list, int *error,
int *cache_time, __unused char **stack, __unused char ***stkptr)
{
int res;
struct dir_cbdata readdir_cbdata;
if (trace > 1)
trace_prt(1, "getmapkeys_od called\n");
*cache_time = RDDIR_CACHE_TIME;
*error = 0;
if (trace > 1)
trace_prt(1, "getmapkeys_od: Requesting list in %s\n",
nsmap);
readdir_cbdata.list = list;
readdir_cbdata.last = NULL;
readdir_cbdata.error = 0;
res = od_search(kODAttributeTypeMetaAutomountMap, nsmap,
readdir_callback, (void *) &readdir_cbdata);
if (trace > 1)
trace_prt(1, " getmapkeys_od: od_search returned %d\n",
res);
if (readdir_cbdata.error)
*error = readdir_cbdata.error;
if (res != __NSW_SUCCESS) {
if (*error == 0)
*error = EIO;
}
return (res);
}
static callback_ret_t
readdir_callback(CFStringRef inkey, CFStringRef value, void *udata)
{
char *str;
CFIndex inkeylen, value_len;
struct dir_cbdata *temp = (struct dir_cbdata *)udata;
struct dir_entry **list = temp->list;
struct dir_entry *last = temp->last;
char key[MAXFILENAMELEN+1];
char linebuf[LINESZ], lineqbuf[LINESZ];
int error;
if (trace > 1) {
str = od_CFStringtoCString(inkey);
if (str != NULL) {
trace_prt(1, " readdir_callback called: key %s\n",
str);
free(str);
}
}
inkeylen = od_cfstrlen(inkey);
if (inkeylen > (CFIndex)MAXFILENAMELEN)
return (OD_CB_KEEPGOING);
if (inkeylen == 0)
return (OD_CB_KEEPGOING);
if (!od_cfstrlcpy(key, inkey, inkeylen))
return (OD_CB_KEEPGOING);
if (isspace((unsigned char)key[0]) || key[0] == '#')
return (OD_CB_KEEPGOING);
if (key[0] == '*' && key[1] == '\0')
return (OD_CB_KEEPGOING);
value_len = od_cfstrlen(value);
if (value_len >= LINESZ)
return (OD_CB_KEEPGOING);
if (value_len < 2)
return (OD_CB_KEEPGOING);
if (!od_cfstrlcpy(linebuf, value, value_len))
return (OD_CB_KEEPGOING);
unquote(linebuf, lineqbuf);
error = add_dir_entry(key, linebuf, lineqbuf, list, &last);
if (error != -1) {
if (error != 0) {
temp->error = error;
return (OD_CB_ERROR);
}
temp->last = last;
}
if (trace > 1)
trace_prt(1, "readdir_callback returning OD_CB_KEEPGOING...\n");
return (OD_CB_KEEPGOING);
}
static callback_ret_t
od_process_record_attributes(ODRecordRef record, callback_fn callback,
void *udata)
{
CFErrorRef error;
char *errstring;
CFArrayRef keys;
CFStringRef key;
CFArrayRef values;
CFStringRef value;
callback_ret_t ret;
if (trace > 1) {
trace_prt(1,
"od_process_record_attributes entered\n");
}
error = NULL;
keys = ODRecordCopyValues(record, kODAttributeTypeRecordName, &error);
if (keys == NULL) {
if (error != NULL) {
errstring = od_get_error_string(error);
pr_msg("od_process_record_attributes: can't get kODAttributeTypeRecordName attribute for record: %s",
errstring);
free(errstring);
return (OD_CB_ERROR);
} else {
pr_msg("od_process_record_attributes: record has no kODAttributeTypeRecordName attribute");
return (OD_CB_REJECTED);
}
}
if (CFArrayGetCount(keys) == 0) {
CFRelease(keys);
pr_msg("od_process_record_attributes: record has no kODAttributeTypeRecordName attribute");
return (OD_CB_REJECTED);
}
key = CFArrayGetValueAtIndex(keys, 0);
error = NULL;
values = ODRecordCopyValues(record,
kODAttributeTypeAutomountInformation, &error);
if (values == NULL) {
CFRelease(keys);
if (error != NULL) {
errstring = od_get_error_string(error);
pr_msg("od_process_record_attributes: can't get kODAttributeTypeAutomountInformation attribute for record: %s",
errstring);
free(errstring);
return (OD_CB_ERROR);
} else {
pr_msg("od_process_record_attributes: record has no kODAttributeTypeAutomountInformation attribute");
return (OD_CB_REJECTED);
}
}
if (CFArrayGetCount(values) == 0) {
CFRelease(values);
CFRelease(keys);
pr_msg("od_process_record_attributes: record has no kODAttributeTypeRecordName attribute");
return (OD_CB_REJECTED);
}
value = CFArrayGetValueAtIndex(values, 0);
ret = (*callback)(key, value, udata);
CFRelease(values);
CFRelease(keys);
return (ret);
}
static int
od_search(CFStringRef attr_to_match, char *value_to_match, callback_fn callback,
void *udata)
{
int ret;
CFErrorRef error;
char *errstring;
ODNodeRef node_ref;
CFArrayRef attrs;
CFStringRef value_to_match_cfstr;
ODQueryRef query_ref;
CFArrayRef results;
CFIndex num_results;
CFIndex i;
ODRecordRef record;
callback_ret_t callback_ret;
error = NULL;
node_ref = ODNodeCreateWithNodeType(kCFAllocatorDefault, kODSessionDefault,
kODNodeTypeAuthentication, &error);
if (node_ref == NULL) {
errstring = od_get_error_string(error);
pr_msg("od_search: can't create search node for /Search: %s",
errstring);
free(errstring);
return (__NSW_UNAVAIL);
}
value_to_match_cfstr = CFStringCreateWithCString(kCFAllocatorDefault,
value_to_match, kCFStringEncodingUTF8);
if (value_to_match_cfstr == NULL) {
CFRelease(node_ref);
pr_msg("od_search: can't make CFString from %s",
value_to_match);
return (__NSW_UNAVAIL);
}
attrs = CFArrayCreate(kCFAllocatorDefault,
(const void *[2]){kODAttributeTypeRecordName,
kODAttributeTypeAutomountInformation}, 2,
&kCFTypeArrayCallBacks);
if (attrs == NULL) {
CFRelease(value_to_match_cfstr);
CFRelease(node_ref);
pr_msg("od_search: can't make array of attribute types");
return (__NSW_UNAVAIL);
}
error = NULL;
query_ref = ODQueryCreateWithNode(kCFAllocatorDefault, node_ref,
kODRecordTypeAutomount, attr_to_match, kODMatchEqualTo,
value_to_match_cfstr, attrs, 0, &error);
CFRelease(attrs);
CFRelease(value_to_match_cfstr);
if (query_ref == NULL) {
CFRelease(node_ref);
errstring = od_get_error_string(error);
pr_msg("od_search: can't create query: %s",
errstring);
free(errstring);
return (__NSW_UNAVAIL);
}
error = NULL;
results = ODQueryCopyResults(query_ref, false, &error);
if (results == NULL) {
CFRelease(query_ref);
CFRelease(node_ref);
errstring = od_get_error_string(error);
pr_msg("od_search: query failed: %s", errstring);
free(errstring);
return (__NSW_UNAVAIL);
}
ret = __NSW_NOTFOUND;
num_results = CFArrayGetCount(results);
for (i = 0; i < num_results; i++) {
record = (ODRecordRef)CFArrayGetValueAtIndex(results, i);
callback_ret = od_process_record_attributes(record,
callback, udata);
if (callback_ret == OD_CB_KEEPGOING) {
ret = __NSW_SUCCESS;
} else if (callback_ret == OD_CB_DONE) {
ret = __NSW_SUCCESS;
break;
} else if (callback_ret == OD_CB_ERROR) {
ret = __NSW_UNAVAIL;
break;
}
}
CFRelease(results);
CFRelease(query_ref);
CFRelease(node_ref);
return (ret);
}