int open64(const char *path, int flags, ...) { char *rpath, *strflags=str_flags(flags); va_list argptr; mode_t mode; int found; // If O_CREAT then mode is not set. if (flags & O_CREAT) { va_start(argptr, flags); mode=va_arg(argptr, mode_t); va_end(argptr); } else { mode=0; } sb_log(2, "Start open64(\"%s\", %s, %04o).", path, strflags, mode); rpath=rewrite(path, R_FILE); if ((found=has_access(rpath)) || ! (flags & (O_WRONLY|O_RDWR))) { // Disable O_CREAT when O_RDONLY if (!found) { flags&=~O_CREAT; strflags=str_flags(flags); } sb_log(4, "Do open64(\"%s\", %s, %04o).", path, strflags, mode); return _real_open64(path, flags, mode); } if (action==A_HALT) exit(0); sb_log(0, "Attempt to open64(\"%s\", %s, %04o).", rpath, strflags, mode); free(rpath); free(strflags); if (action==A_WARN) return devnull; errno=EACCES; return -1; }
int rename(const char *oldpath, const char *newpath) { char *oldrpath, *newrpath; sb_log(2, "Start rename(\"%s\", \"%s\").", oldpath, newpath); oldrpath=rewrite(oldpath, R_LINK); newrpath=rewrite(newpath, R_FILE); if (has_access(oldrpath) && has_access(newrpath)) { sb_log(4, "Do rename(\"%s\", \"%s\").", oldpath, newpath); return _real_rename(oldpath, newpath); } if (action==A_HALT) exit(0); sb_log(0, "Attempt to rename(\"%s\", \"%s\").", oldrpath, newrpath); free(oldrpath); free(newrpath); if (action==A_WARN) return 0; errno=EACCES; return -1; }
int utimes(const char *path, const struct timeval *tvp) { char *rpath; sb_log(2, "Start utimes(\"%s\", NULL).", path); rpath=rewrite(path, R_FILE); if (has_access(rpath)) { sb_log(4, "Do utimes(\"%s\", NULL).", path); return _real_utimes(path, tvp); } if (action==A_HALT) exit(0); sb_log(0, "Attempt to utimes(\"%s\", NULL).", rpath); free(rpath); if (action==A_WARN) return 0; errno=EACCES; return -1; }
int unlink(const char *path) { char *rpath; sb_log(2, "Start unlink(\"%s\").", path); rpath=rewrite(path, R_LINK); if (has_access(rpath)) { sb_log(4, "Do unlink(\"%s\").", path); return _real_unlink(path); } if (action==A_HALT) exit(0); sb_log(0, "Attempt to unlink(\"%s\").", rpath); free(rpath); if (action==A_WARN) return 0; errno=EACCES; return -1; }
int creat64(const char *path, mode_t mode) { char *rpath; sb_log(2, "Start creat64(\"%s\", %04o).", path, mode); rpath=rewrite(path, R_FILE); if (has_access(rpath)) { sb_log(4, "Do creat64(\"%s\", %04o).", path, mode); return _real_creat64(path, mode); } if (action==A_HALT) exit(0); sb_log(0, "Attempt to creat64(\"%s\", %04o).", rpath, mode); free(rpath); if (action==A_WARN) return devnull; errno=EACCES; return -1; }
int __xmknod(int ver, const char *path, mode_t mode, dev_t *dev) { char *rpath; sb_log(2, "Start __xmknod(%i, \"%s\", %04o).", ver, path, mode); rpath=rewrite(path, R_LINK); if (has_access(rpath)) { sb_log(4, "Do __xmknod(%i, \"%s\", %04o).", ver, path, mode); return _real___xmknod(ver, path, mode, dev); } if (action==A_HALT) exit(0); sb_log(0, "Attempt to __xmknod(%i, \"%s\", %04o).", ver, rpath, mode); free(rpath); if (action==A_WARN) return 0; errno=EACCES; return -1; }
int lchown(const char *path, uid_t owner, gid_t group) { char *rpath; sb_log(2, "Start lchown(\"%s\", %i, %i).", path, owner, group); rpath=rewrite(path, R_LINK); if (has_access(rpath)) { sb_log(4, "Do lchown(\"%s\", %i, %i).", path, owner, group); return _real_lchown(path, owner, group); } if (action==A_HALT) exit(0); sb_log(0, "Attempt to lchown(\"%s\", %i, %i).", rpath, owner, group); free(rpath); if (action==A_WARN) return 0; errno=EACCES; return -1; }
int symlink(const char *oldpath, const char *newpath) { // char *oldrpath; char *newrpath; sb_log(2, "Start symlink(\"%s\", \"%s\").", oldpath, newpath); // oldrpath=rewrite(oldpath, R_FILE); newrpath=rewrite(newpath, R_LINK); if (has_access(newrpath)) { sb_log(4, "Do symlink(\"%s\", \"%s\").", oldpath, newpath); return _real_symlink(oldpath, newpath); } if (action==A_HALT) exit(0); sb_log(0, "Attempt to symlink(\"%s\", \"%s\").", oldpath, newrpath); free(newrpath); if (action==A_WARN) return 0; errno=EACCES; return -1; }
static bool bucket_list_keys(struct client *cli, const char *user, const char *bucket) { GHashTable *param; enum errcode err = InternalError; char *prefix, *marker, *maxkeys_str, *delim, *s; int maxkeys = 100, i, rc; GList *content, *tmpl; size_t pfx_len; struct bucket_list_info bli; bool rcb; DB_ENV *dbenv = tdbrep.tdb.env; DB_TXN *txn = NULL; DB *objs = tdbrep.tdb.objs; DBC *cur = NULL; DBT pkey, pval; struct db_obj_key *obj_key; size_t alloc_len; bool seen_prefix = false; int get_flags; /* verify READ access */ if (!user || !has_access(user, bucket, NULL, "READ")) { err = AccessDenied; goto err_out; } /* parse URI query string */ param = hreq_query(&cli->req); if (!param) goto err_out; /* read useful params from query string */ prefix = g_hash_table_lookup(param, "prefix"); pfx_len = prefix ? strlen(prefix) : 0; marker = g_hash_table_lookup(param, "marker"); delim = g_hash_table_lookup(param, "delimiter"); maxkeys_str = g_hash_table_lookup(param, "max-keys"); if (maxkeys_str) { i = atoi(maxkeys_str); if (i > 0 && i < maxkeys) maxkeys = i; } /* open transaction */ rc = dbenv->txn_begin(dbenv, NULL, &txn, 0); if (rc) { dbenv->err(dbenv, rc, "DB_ENV->txn_begin"); goto err_out; } /* search for (bucket, *) in object database, to see if * any objects associated with this bucket exist */ rc = objs->cursor(objs, txn, &cur, 0); if (rc) { objs->err(objs, rc, "objs->cursor"); goto err_out; } alloc_len = sizeof(*obj_key) + (marker ? strlen(marker) : pfx_len) + 1; obj_key = alloca(alloc_len); memset(obj_key, 0, alloc_len); strncpy(obj_key->bucket, bucket, sizeof(obj_key->bucket)); strcpy(obj_key->key, marker ? marker : prefix ? prefix : ""); memset(&pkey, 0, sizeof(pkey)); pkey.data = obj_key; pkey.size = alloc_len; memset(&bli, 0, sizeof(bli)); bli.prefix = prefix; bli.pfx_len = pfx_len; bli.delim = delim; bli.common_pfx = g_hash_table_new_full(g_str_hash, g_str_equal, free, NULL); bli.maxkeys = maxkeys; /* iterate through each returned data row */ get_flags = DB_SET_RANGE; while (1) { struct obj_vitals v; struct db_obj_key *tmpkey; struct db_obj_ent *obj; memset(&pval, 0, sizeof(pval)); pval.flags = DB_DBT_MALLOC; rc = cur->get(cur, &pkey, &pval, get_flags); if (rc) { if (rc != DB_NOTFOUND) objs->err(objs, rc, "bucket_list_keys iter"); break; } get_flags = DB_NEXT; tmpkey = pkey.data; obj = pval.data; if (strcmp(tmpkey->bucket, bucket)) { free(obj); break; } if (prefix) { if (strncmp(tmpkey->key, prefix, pfx_len) != 0) { free(obj); if (!seen_prefix) /* continue searching for * a record that begins with this * prefix */ continue; else /* no more records with our prefix */ break; } seen_prefix = true; } memset(&v, 0, sizeof(v)); strcpy(v.md5, obj->md5); strncpy(v.owner, obj->owner, sizeof(v.owner)-1); if (!(GUINT32_FROM_LE(obj->flags) & DB_OBJ_INLINE)) memcpy(&v.addr, &obj->d.a, sizeof(v.addr)); v.mtime = GUINT64_FROM_LE(obj->mtime); v.size = GUINT64_FROM_LE(obj->size); free(obj); if (bucket_list_iter(tmpkey->key, &v, &bli)) break; } /* close cursor, transaction */ rc = cur->close(cur); if (rc) { objs->err(objs, rc, "objs->cursor close"); goto err_out_rb; } rc = txn->commit(txn, 0); if (rc) { dbenv->err(dbenv, rc, "DB_ENV->txn_commit"); goto err_out_param; } s = g_markup_printf_escaped( "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" "<ListBucketResult xmlns=\"http://indy.yyz.us/doc/2006-03-01/\">\r\n" " <Name>%s</Name>\r\n" " <MaxKeys>%d</MaxKeys>\r\n" " <IsTruncated>%s</IsTruncated>\r\n", bucket, maxkeys, bli.trunc ? "true" : "false"); content = g_list_append(NULL, s); if (prefix) { s = g_markup_printf_escaped(" <Prefix>%s</Prefix>\n", prefix); content = g_list_append(content, s); } if (marker) { s = g_markup_printf_escaped(" <Marker>%s</Marker>\n", marker); content = g_list_append(content, s); } tmpl = bli.res; while (tmpl) { char timestr[64]; struct obj_vitals *vp; vp = tmpl->data; tmpl = tmpl->next; /* * FIXME Use the vp->addr to verify that key still exists. * And if it doesn't, then what? (addr.nid can be 0 for inline) */ s = g_markup_printf_escaped( " <Contents>\r\n" " <Key>%s</Key>\r\n" " <LastModified>%s</LastModified>\r\n" " <ETag>%s</ETag>\r\n" " <Size>%llu</Size>\r\n" " <StorageClass>STANDARD</StorageClass>\r\n" " <Owner>\r\n" " <ID>%s</ID>\r\n" " <DisplayName>%s</DisplayName>\r\n" " </Owner>\r\n" " </Contents>\r\n", vp->key, hutil_time2str(timestr, sizeof(timestr), vp->mtime / 1000000), vp->md5, (unsigned long long) vp->size, vp->owner, vp->owner); content = g_list_append(content, s); free(vp->key); free(vp); } g_list_free(bli.res); content = bucket_list_pfx(content, bli.common_pfx, bli.delim); s = strdup("</ListBucketResult>\r\n"); content = g_list_append(content, s); free(bli.last_comp); g_hash_table_destroy(bli.common_pfx); g_hash_table_destroy(param); rcb = cli_resp_xml(cli, 200, content); g_list_free(content); return rcb; err_out_rb: rc = txn->abort(txn); if (rc) dbenv->err(dbenv, rc, "DB_ENV->txn_abort"); err_out_param: g_hash_table_destroy(param); err_out: return cli_err(cli, err); }
bool bucket_add(struct client *cli, const char *user, const char *bucket) { char *hdr, timestr[64]; enum errcode err = InternalError; int rc; struct db_bucket_ent ent; bool setacl; /* is ok to put pre-existing bucket */ enum ReqACLC canacl; DB *buckets = tdbrep.tdb.buckets; DB *acls = tdbrep.tdb.acls; DB_ENV *dbenv = tdbrep.tdb.env; DB_TXN *txn = NULL; DBT key, val; if (!user) return cli_err(cli, AccessDenied); /* prepare parameters */ setacl = false; if (cli->req.uri.query_len) { switch (hreq_is_query(&cli->req)) { case URIQ_ACL: setacl = true; break; default: err = InvalidURI; goto err_par; } } if ((rc = hreq_acl_canned(&cli->req)) == ACLCNUM) { err = InvalidArgument; goto err_par; } canacl = (rc == -1)? ACLC_PRIV: rc; /* begin trans */ rc = dbenv->txn_begin(dbenv, NULL, &txn, 0); if (rc) { dbenv->err(dbenv, rc, "DB_ENV->txn_begin"); goto err_db; } memset(&key, 0, sizeof(key)); memset(&val, 0, sizeof(val)); memset(&ent, 0, sizeof(ent)); strncpy(ent.name, bucket, sizeof(ent.name)); strncpy(ent.owner, user, sizeof(ent.owner)); ent.time_create = GUINT64_TO_LE(time(NULL)); key.data = &ent.name; key.size = strlen(ent.name) + 1; val.data = &ent; val.size = sizeof(ent); if (setacl) { /* check if the bucket exists, else insert it */ rc = bucket_find(txn, bucket, NULL, 0); if (rc) { if (rc != DB_NOTFOUND) { buckets->err(buckets, rc, "buckets->find"); goto err_out; } rc = buckets->put(buckets, txn, &key, &val, DB_NOOVERWRITE); if (rc) { buckets->err(buckets, rc, "buckets->put"); goto err_out; } } else { if (!has_access(user, bucket, NULL, "WRITE_ACP")) { err = AccessDenied; goto err_out; } if (!object_del_acls(txn, bucket, "")) goto err_out; } } else { /* attempt to insert new bucket */ rc = buckets->put(buckets, txn, &key, &val, DB_NOOVERWRITE); if (rc) { if (rc == DB_KEYEXIST) err = BucketAlreadyExists; else buckets->err(buckets, rc, "buckets->put"); goto err_out; } } /* insert bucket ACL */ rc = add_access_canned(txn, bucket, "", user, canacl); if (rc) { acls->err(acls, rc, "acls->put"); goto err_out; } /* commit -- no more exception emulation with goto. */ rc = txn->commit(txn, 0); if (rc) { dbenv->err(dbenv, rc, "DB_ENV->txn_commit"); return cli_err(cli, InternalError); } if (asprintf(&hdr, "HTTP/%d.%d 200 x\r\n" "Content-Length: 0\r\n" "Date: %s\r\n" "Location: /%s\r\n" "Server: " PACKAGE_STRING "\r\n" "\r\n", cli->req.major, cli->req.minor, hutil_time2str(timestr, sizeof(timestr), time(NULL)), bucket) < 0) return cli_err(cli, InternalError); rc = atcp_writeq(&cli->wst, hdr, strlen(hdr), atcp_cb_free, hdr); if (rc) { free(hdr); return true; } return atcp_write_start(&cli->wst); err_out: rc = txn->abort(txn); if (rc) dbenv->err(dbenv, rc, "DB_ENV->txn_abort"); err_db: err_par: return cli_err(cli, err); }
bool access_list(struct client *cli, const char *bucket, const char *key, const char *user) { struct macl { char perm[128]; /* perm(s) granted */ char grantee[64]; /* grantee user */ }; GHashTable *param; enum errcode err = InternalError; DB_ENV *dbenv = tdbrep.tdb.env; DB *acls = tdbrep.tdb.acls; int alloc_len; char owner[64]; GList *res; struct db_acl_key *acl_key; struct db_acl_ent *acl; DB_TXN *txn = NULL; DBC *cur = NULL; GList *content; DBT pkey, pval; struct macl *mp; char guser[64]; GList *p; char *s; int str_len; int rc; bool rcb; /* verify READ access for ACL */ if (!user || !has_access(user, bucket, key, "READ_ACP")) { err = AccessDenied; goto err_out; } /* parse URI query string */ param = hreq_query(&cli->req); if (!param) goto err_out; res = NULL; alloc_len = sizeof(struct db_acl_key) + strlen(key) + 1; acl_key = alloca(alloc_len); memset(acl_key, 0, alloc_len); strncpy(acl_key->bucket, bucket, sizeof(acl_key->bucket)); strcpy(acl_key->key, key); /* open transaction, search cursor */ rc = dbenv->txn_begin(dbenv, NULL, &txn, 0); if (rc) { dbenv->err(dbenv, rc, "DB_ENV->txn_begin"); goto err_out_param; } rc = bucket_find(txn, bucket, &owner[0], sizeof(owner)); if (rc) { if (rc == DB_NOTFOUND) err = InvalidBucketName; else dbenv->err(dbenv, rc, "bucket_find"); goto err_out_rb; } rc = acls->cursor(acls, txn, &cur, 0); if (rc) { acls->err(acls, rc, "acls->cursor"); goto err_out_rb; } memset(&pkey, 0, sizeof(pkey)); pkey.data = acl_key; pkey.size = alloc_len; for (;; free(acl)) { memset(&pval, 0, sizeof(pval)); pval.flags = DB_DBT_MALLOC; rc = cur->get(cur, &pkey, &pval, DB_NEXT); if (rc) break; acl = pval.data; /* This is a workaround, see FIXME about DB_NEXT. */ if (strncmp(acl->bucket, bucket, sizeof(acl->bucket))) continue; if (strcmp(acl->key, key)) continue; if ((mp = malloc(sizeof(struct macl))) == NULL) { free(acl); cur->close(cur); goto err_out_rb; } memcpy(mp->grantee, acl->grantee, sizeof(mp->grantee)); mp->grantee[sizeof(mp->grantee)-1] = 0; memcpy(mp->perm, acl->perm, sizeof(mp->perm)); /* lop off the trailing comma */ mp->perm[sizeof(mp->perm)-1] = 0; str_len = strlen(mp->perm); if (str_len && mp->perm[str_len-1] == ',') mp->perm[--str_len] = 0; res = g_list_append(res, mp); } if (rc != DB_NOTFOUND) acls->err(acls, rc, "access_list iteration"); /* close cursor, transaction */ rc = cur->close(cur); if (rc) acls->err(acls, rc, "acls->cursor close"); rc = txn->commit(txn, 0); if (rc) dbenv->err(dbenv, rc, "DB_ENV->txn_commit"); /* dump collected acls -- no more exception handling */ s = g_markup_printf_escaped( "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" "<AccessControlPolicy " "xmlns=\"http://indy.yyz.us/doc/2006-03-01/\">\r\n" " <Owner>\r\n" " <ID>%s</ID>\r\n" " <DisplayName>%s</DisplayName>\r\n" " </Owner>\r\n", owner, owner); content = g_list_append(NULL, s); s = g_markup_printf_escaped( " <AccessControlList>\r\n"); content = g_list_append(content, s); for (p = res; p != NULL; p = p->next) { mp = p->data; if (!strcmp(DB_ACL_ANON, mp->grantee)) { strcpy(guser, "anonymous"); } else { strncpy(guser, mp->grantee, sizeof(guser)); guser[sizeof(guser)-1] = 0; } s = g_markup_printf_escaped( " <Grant>\r\n" " <Grantee xmlns:xsi=\"http://www.w3.org/2001/" "XMLSchema-instance\" xsi:type=\"CanonicalUser\">\r\n" " <ID>%s</ID>\r\n" " <DisplayName>%s</DisplayName>\r\n" " </Grantee>\r\n", guser, guser); content = g_list_append(content, s); /* * FIXME This parsing is totally lame, we should replace * strings with a bit mask once we make sure this works. */ if (!strcmp(mp->perm, "READ,WRITE,READ_ACP,WRITE_ACP")) { s = g_markup_printf_escaped( " <Permission>FULL_CONTROL</Permission>\r\n"); } else { s = g_markup_printf_escaped( " <Permission>%s</Permission>\r\n", mp->perm); } content = g_list_append(content, s); s = g_markup_printf_escaped(" </Grant>\r\n"); content = g_list_append(content, s); free(mp); } s = g_markup_printf_escaped(" </AccessControlList>\r\n"); content = g_list_append(content, s); s = g_markup_printf_escaped("</AccessControlPolicy>\r\n"); content = g_list_append(content, s); g_list_free(res); rcb = cli_resp_xml(cli, 200, content); g_list_free(content); return rcb; err_out_rb: rc = txn->abort(txn); if (rc) dbenv->err(dbenv, rc, "DB_ENV->txn_abort"); for (p = res; p != NULL; p = p->next) free(p->data); g_list_free(res); err_out_param: g_hash_table_destroy(param); err_out: return cli_err(cli, err); }