/** * Copyright (C) 2006 International Business Machines Corp. * Author(s): Michael A. Halcrow * Trevor Highland * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include "config.h" #ifndef S_SPLINT_S #include #include #endif /* S_SPLINT_S */ #include #include #include #ifndef S_SPLINT_S #include #include #endif /* S_SPLINT_S */ #include #include #include #include "../include/ecryptfs.h" #define MAX_TOK_LEN 128 #define MAX_FILE_SIZE 0xa000 int print_nvp_list(struct ecryptfs_name_val_pair *dst) { syslog(LOG_ERR, "Printing nvp list\n"); while (dst) { syslog(LOG_ERR, "name=%s\n", dst->name); syslog(LOG_ERR, "val=%s\n", dst->value); dst = dst->next; } return 0; } static int copy_nv_pair(struct ecryptfs_name_val_pair *dst, struct ecryptfs_name_val_pair *src) { int rc; dst->flags = src->flags; if ((rc = asprintf(&dst->name, "%s", src->name)) == -1) { rc = -ENOMEM; goto out; } if ((rc = asprintf(&dst->value, "%s", src->value)) == -1) { rc = -ENOMEM; goto out; } rc = 0; out: return rc; } /** * For each name in dst that is also in src, set the value in dst to * that which is in src. * * For each name in src that is not in dst, append a copy of the node * onto dst. * * TODO: This function sucks. Partially because the nv data structure * sucks. Overhaul it sometime. */ int ecryptfs_nvp_list_union(struct ecryptfs_name_val_pair *dst, struct ecryptfs_name_val_pair *src, struct ecryptfs_name_val_pair *allowed_duplicates) { int rc = 0; struct ecryptfs_name_val_pair *dst_cursor; struct ecryptfs_name_val_pair *src_cursor; src_cursor = src->next; while (src_cursor) { int found_match; struct ecryptfs_name_val_pair *prev_dst_cursor; struct ecryptfs_name_val_pair *ad_cursor; if (!src_cursor->name) goto next_src_cursor; found_match = 0; prev_dst_cursor = dst; dst_cursor = dst->next; ad_cursor = allowed_duplicates->next; while (ad_cursor) { if (strcmp(src_cursor->name, ad_cursor->name) == 0) { if (ecryptfs_verbosity) syslog(LOG_INFO, "Duplicates allowed for [%s]\n", src_cursor->name); while (dst_cursor) { prev_dst_cursor = dst_cursor; dst_cursor = dst_cursor->next; } goto do_insert; } ad_cursor = ad_cursor->next; } while (dst_cursor) { if (!dst_cursor->name) goto next_dst_cursor; if (strcmp(src_cursor->name, dst_cursor->name) == 0) { found_match = 1; free(dst_cursor->value); rc = asprintf(&dst_cursor->value, "%s", src_cursor->value); if (rc == -1) { rc = -ENOMEM; goto out; } } next_dst_cursor: prev_dst_cursor = dst_cursor; dst_cursor = dst_cursor->next; } do_insert: if (!found_match) { struct ecryptfs_name_val_pair *dst_tmp; struct ecryptfs_name_val_pair *src_tmp; int i; prev_dst_cursor->next = dst_cursor = malloc(sizeof(struct ecryptfs_name_val_pair)); memset(dst_cursor, 0, sizeof(struct ecryptfs_name_val_pair)); if (!dst_cursor) { rc = -ENOMEM; goto out; } dst_cursor->next = NULL; if ((rc = copy_nv_pair(dst_cursor, src_cursor))) { goto out; } dst_tmp = dst_cursor; src_tmp = src_cursor; /* TODO: Okay; this has officially become a * hack. It's time to switch to a real tree * structure for the name/value pair list. */ for (i = 0; i < NV_MAX_CHILDREN; i++) { if (src_cursor->children[i]) { if ((dst_cursor->children[i] = malloc(sizeof(struct ecryptfs_name_val_pair))) == NULL) { rc = -ENOMEM; goto out; } memset(dst_cursor->children[i], 0, sizeof(struct ecryptfs_name_val_pair)); copy_nv_pair(dst_cursor->children[i], src_cursor->children[i]); dst_tmp->next = dst_cursor->children[i]; prev_dst_cursor = dst_tmp; dst_tmp = dst_tmp->next; prev_dst_cursor->next = dst_tmp; src_tmp = src_tmp->next; if (src_tmp != src_cursor->children[i]) { rc = -EINVAL; syslog(LOG_ERR, "Internal error: src_tmp" "->next != src_cursor->c" "hildren[%d]\n", i); goto out; } } } dst_cursor = dst_tmp; src_cursor = src_tmp; } next_src_cursor: src_cursor = src_cursor->next; } out: return rc; } int ecryptfs_parse_rc_file_fullpath(struct ecryptfs_name_val_pair *nvp_list_head, char *fullpath) { int rc; int fd; fd = open(fullpath, O_RDONLY); if (fd == -1) { rc = -EIO; goto out; } rc = parse_options_file(fd, nvp_list_head); close(fd); out: return rc; } int ecryptfs_parse_rc_file(struct ecryptfs_name_val_pair *nvp_list_head) { char *home; uid_t uid; struct passwd *pw; char *rcfile_fullpath; int rc; uid = getuid(); pw = getpwuid(uid); if (!pw) { rc = -EIO; goto out; } home = pw->pw_dir; rc = asprintf(&rcfile_fullpath, "%s/.ecryptfsrc", home); if (rc == -1) { rc = -ENOMEM; goto out; } rc = ecryptfs_parse_rc_file_fullpath(nvp_list_head, rcfile_fullpath); free(rcfile_fullpath); out: return rc; } int process_comma_tok(struct ecryptfs_name_val_pair **current, char *tok, /*@null@*/ char *prefix) { int tok_len = (int)strlen(tok); char new_prefix[MAX_TOK_LEN]; char sub_token[MAX_TOK_LEN]; char *name = NULL; char *value = NULL; int i, j, st_len; int rc = 0; if(tok && tok[0] == '\0') { goto out; } if (tok_len < 0 || tok_len > MAX_TOK_LEN) { rc = -EINVAL; goto out; } if (tok[0] == '=' || tok[0] == ':') { rc = -EINVAL; goto out; } j = 0; if (tok_len > 4 && !memcmp(tok, "key=", 4)) for (i = 4; i < tok_len; i++) { if (tok[i] == ':') goto process_colon_list; } goto process_nv_pair; process_colon_list: new_prefix[j] = '\0'; i = 0; j = 0; while (i < tok_len) { if (tok[i] == ':') { sub_token[j] = '\0'; if ((rc = process_comma_tok(current, sub_token, NULL))) goto out; j = 0; } else sub_token[j++] = tok[i]; i++; } sub_token[j] = '\0'; rc = process_comma_tok(current, sub_token, new_prefix); goto out; process_nv_pair: st_len = snprintf(sub_token, MAX_TOK_LEN, "%s%s", (prefix ? prefix : ""), tok); j = 0; for (i = 0; i < st_len; i++) if (sub_token[i] == '=') { if (!(name = malloc(i + 1))) { rc = -ENOMEM; goto out; } memcpy(name, sub_token, i); name[i] = '\0'; j = i; } if (!name) { if (!(name = malloc(i+1))) { rc = -ENOMEM; goto out; } memcpy(name, sub_token, i); name[i] = '\0'; } else { if((i-j) > 1) { if (!(value = malloc(i - j + 1))) { rc = -ENOMEM; goto out; } memcpy(value, &sub_token[j+1], (i - j)); value[(i - j)] = '\0'; } } if (!((*current)->next = malloc(sizeof(struct ecryptfs_name_val_pair)))) { rc = -ENOMEM; goto out; } memset((*current)->next, 0, sizeof(struct ecryptfs_name_val_pair)); if (strlen(name) == 0) { free(name); free(value); } else { *current = (*current)->next; (*current)->name = name; (*current)->value = value; (*current)->next = NULL; } out: return rc; } /** * name=val,key=PKI:name1=val1:name2=val2,name=val... */ int generate_nv_list(struct ecryptfs_name_val_pair *head, char *buf) { struct ecryptfs_name_val_pair *current = head; char tok_str[MAX_TOK_LEN]; if (!buf) return 0; int buf_len = strlen(buf); int i, j = 0; int rc = 0; for (i = 0; i < buf_len; i++) { if (buf[i] == ',' || buf[i] == '\n') { tok_str[j] = '\0'; if ((rc = process_comma_tok(¤t, tok_str, NULL))) goto out; j = 0; } else tok_str[j++] = buf[i]; if (j == MAX_TOK_LEN) goto out; } tok_str[j] = '\0'; if ((rc = process_comma_tok(¤t, tok_str, NULL))) goto out; out: return rc; } int ecryptfs_parse_options(char *opts, struct ecryptfs_name_val_pair *head) { return generate_nv_list(head, opts); } int parse_options_file(int fd, struct ecryptfs_name_val_pair *head) { int rc = 0; int pagesize; char *data; off_t file_size; struct stat filestat; rc = fstat(fd, &filestat); if (rc) { syslog(LOG_ERR, "%s: fstat returned [%d] on fd [%d]\n", __FUNCTION__, rc, fd); goto out; } pagesize = getpagesize(); file_size = filestat.st_size; if (file_size > MAX_FILE_SIZE) { syslog(LOG_ERR, "File size too large\n"); rc = -1; goto out; } if (file_size % pagesize) { file_size -= file_size % pagesize; file_size += pagesize; } data = mmap((caddr_t)0, file_size, PROT_READ, MAP_PRIVATE, fd, 0); if (data == MAP_FAILED) { rc = errno; syslog(LOG_ERR, "%s: mmap failed on fd [%d]; rc = [%d]\n", __FUNCTION__, fd, rc); goto out; } rc = generate_nv_list(head, data); munmap(data, file_size); out: return rc; }