void mount_fstab() { FILE *fstab = fopen(jail_dir(".fstab"), "r"); size_t line_len = 0; char *line = NULL; size_t read_len; if(fstab == NULL) { perror("Can't open fstab"); exit(-1); } while((read_len = getline(&line, &line_len, fstab)) != -1) { char *buf = line; char *src = strsep(&buf, ":"); char *target = strsep(&buf, ":"); char *fs = strsep(&buf, ":"); int flags = atoi(strsep(&buf, ":")); if(flags & MS_BIND) { bind_mount(src, jail_dir(target), flags); } else { check(aasprintf("mount: %s -> %s (%s) %d %s", src, jail_dir(target), fs, flags, buf), mount(src, jail_dir(target), fs, flags, buf)); } } fclose(fstab); if(line) free(line); }
int SLNFilterCopyURI(SLNFilterRef const filter, uint64_t const fileID, bool const meta, DB_txn *const txn, str_t **const out) { DB_val fileID_key[1], file_val[1]; SLNFileByIDKeyPack(fileID_key, txn, fileID); int rc = db_get(txn, fileID_key, file_val); if(rc < 0) return rc; strarg_t const hash = db_read_string(file_val, txn); db_assert(hash); str_t *URI = NULL; if(!meta) { URI = SLNFormatURI(SLN_INTERNAL_ALGO, hash); if(!URI) return DB_ENOMEM; } else { DB_val key[1], val[1]; SLNMetaFileByIDKeyPack(key, txn, fileID); rc = db_get(txn, key, val); if(rc < 0) return rc; uint64_t f; strarg_t target = NULL; SLNMetaFileByIDValUnpack(val, txn, &f, &target); db_assert(target); URI = aasprintf("hash://%s/%s -> %s", SLN_INTERNAL_ALGO, hash, target); if(!URI) return DB_ENOMEM; } *out = URI; URI = NULL; return 0; }
str_t *SLNSessionCopyCookie(SLNSessionRef const session) { if(!session) return NULL; if(!session->sessionKeyRaw) return NULL; str_t hex[SESSION_KEY_HEX+1]; tohex(hex, session->sessionKeyRaw, SESSION_KEY_LEN); hex[SESSION_KEY_HEX] = '\0'; return aasprintf("s=%llu:%s", (unsigned long long)session->sessionID, hex); }
static void md_autolink(cmark_iter *const iter) { regex_t linkify[1]; // <http://daringfireball.net/2010/07/improved_regex_for_matching_urls> // Painstakingly ported to POSIX int rc = regcomp(linkify, "([a-z][a-z0-9_-]+:(/{1,3}|[a-z0-9%])|www[0-9]{0,3}[.]|[a-z0-9.-]+[.][a-z]{2,4}/)([^[:space:]()<>]+|\\(([^[:space:]()<>]+|(\\([^[:space:]()<>]+\\)))*\\))+(\\(([^[:space:]()<>]+|(\\([^[:space:]()<>]+\\)))*\\)|[^][[:space:]`!(){};:'\".,<>?«»“”‘’])", REG_ICASE | REG_EXTENDED); assert(0 == rc); for(;;) { cmark_event_type const event = cmark_iter_next(iter); if(CMARK_EVENT_DONE == event) break; if(CMARK_EVENT_ENTER != event) continue; cmark_node *const node = cmark_iter_get_node(iter); if(CMARK_NODE_TEXT != cmark_node_get_type(node)) continue; char const *const str = cmark_node_get_literal(node); char const *pos = str; regmatch_t match; while(0 == regexec(linkify, pos, 1, &match, 0)) { regoff_t const loc = match.rm_so; regoff_t const len = match.rm_eo - match.rm_so; char *pfx = strndup(pos, loc); char *link_abs = strndup(pos+loc, len); char *link_rel = aasprintf("/history/%s", link_abs); assert(pfx); assert(link_abs); assert(link_rel); cmark_node *text = cmark_node_new(CMARK_NODE_TEXT); cmark_node_set_literal(text, pfx); cmark_node *link = cmark_node_new(CMARK_NODE_LINK); cmark_node_set_url(link, link_rel); cmark_node *sup = superscript("^", "", link_abs); cmark_node *face = cmark_node_new(CMARK_NODE_TEXT); cmark_node_set_literal(face, link_abs); cmark_node_append_child(link, face); cmark_node_insert_before(node, text); cmark_node_insert_before(node, link); cmark_node_insert_before(node, sup); free(pfx); pfx = NULL; free(link_abs); link_abs = NULL; free(link_rel); link_rel = NULL; pos += loc+len; } if(str != pos) { cmark_node *text = cmark_node_new(CMARK_NODE_TEXT); cmark_node_set_literal(text, pos); cmark_node_insert_before(node, text); cmark_node_free(node); } } regfree(linkify); }
BlogRef BlogCreate(SLNRepoRef const repo) { assertf(repo, "Blog requires valid repo"); BlogRef blog = calloc(1, sizeof(struct Blog)); if(!blog) return NULL; blog->repo = repo; blog->dir = aasprintf("%s/blog", SLNRepoGetDir(repo)); blog->cacheDir = aasprintf("%s/blog", SLNRepoGetCacheDir(repo)); if(!blog->dir || !blog->cacheDir) { BlogFree(&blog); return NULL; } // Automatically attempt to create a default blog resource directory. // If it fails, it's probably because it already exists. // If not, we'll find out when we try to load a template. (void)async_fs_symlink(INSTALL_PREFIX "/share/stronglink/blog", blog->dir, 0); if( !load_template(blog, "header.html", &blog->header) || !load_template(blog, "footer.html", &blog->footer) || !load_template(blog, "backlinks.html", &blog->backlinks) || !load_template(blog, "entry-start.html", &blog->entry_start) || !load_template(blog, "entry-end.html", &blog->entry_end) || !load_template(blog, "preview.html", &blog->preview) || !load_template(blog, "empty.html", &blog->empty) || !load_template(blog, "compose.html", &blog->compose) || !load_template(blog, "upload.html", &blog->upload) || !load_template(blog, "login.html", &blog->login) || !load_template(blog, "notfound.html", &blog->notfound) || !load_template(blog, "noresults.html", &blog->noresults) ) { BlogFree(&blog); return NULL; } async_mutex_init(blog->pending_mutex, 0); async_cond_init(blog->pending_cond, 0); return blog; }
SLNPullRef SLNRepoCreatePull(SLNRepoRef const repo, uint64_t const pullID, uint64_t const userID, strarg_t const host, strarg_t const sessionid, strarg_t const query) { SLNPullRef pull = calloc(1, sizeof(struct SLNPull)); if(!pull) return NULL; SLNSessionCacheRef const cache = SLNRepoGetSessionCache(repo); pull->pullID = pullID; pull->session = SLNSessionCreateInternal(cache, 0, NULL, NULL, userID, SLN_RDWR, NULL); // TODO: How to create this properly? pull->host = strdup(host); pull->cookie = aasprintf("s=%s", sessionid ? sessionid : ""); // pull->query = strdup(query); assert(!query || '\0' == query[0]); // TODO if(!pull->session || !pull->host || !pull->cookie) { SLNPullFree(&pull); return NULL; } async_mutex_init(pull->connlock, 0); async_mutex_init(pull->mutex, 0); async_cond_init(pull->cond, 0); pull->stop = true; return pull; }
static int POST_post(BlogRef const blog, SLNSessionRef const session, HTTPConnectionRef const conn, HTTPMethod const method, strarg_t const URI, HTTPHeadersRef const headers) { if(HTTP_POST != method) return -1; if(0 != uripathcmp("/post", URI, NULL)) return -1; // TODO: CSRF token strarg_t const formtype = HTTPHeadersGet(headers, "content-type"); uv_buf_t boundary[1]; int rc = MultipartBoundaryFromType(formtype, boundary); if(rc < 0) return 400; MultipartFormRef form = NULL; rc = MultipartFormCreate(conn, boundary, &form); if(rc < 0) { return 500; } SLNSubmissionRef sub = NULL; SLNSubmissionRef meta = NULL; str_t *title = NULL; rc = parse_file(blog, session, form, &sub, &meta, &title); if(UV_EACCES == rc) { MultipartFormFree(&form); return 403; } if(rc < 0) { MultipartFormFree(&form); return 500; } SLNSubmissionRef extra = NULL; yajl_gen json = NULL; str_t *target_QSEscaped = NULL; str_t *location = NULL; strarg_t const target = SLNSubmissionGetPrimaryURI(sub); if(!target) rc = UV_ENOMEM; if(rc < 0) goto cleanup; target_QSEscaped = QSEscape(target, strlen(target), true); if(!target_QSEscaped) rc = UV_ENOMEM; if(rc < 0) goto cleanup; rc = SLNSubmissionCreate(session, NULL, target, &extra); if(rc < 0) goto cleanup; rc = SLNSubmissionSetType(extra, SLN_META_TYPE); if(rc < 0) goto cleanup; SLNSubmissionWrite(extra, (byte_t const *)target, strlen(target)); SLNSubmissionWrite(extra, (byte_t const *)STR_LEN("\n\n")); json = yajl_gen_alloc(NULL); if(!json) rc = UV_ENOMEM; if(rc < 0) goto cleanup; yajl_gen_config(json, yajl_gen_print_callback, (void (*)())SLNSubmissionWrite, extra); yajl_gen_config(json, yajl_gen_beautify, (int)true); yajl_gen_map_open(json); if(title) { yajl_gen_string(json, (unsigned char const *)STR_LEN("title")); yajl_gen_string(json, (unsigned char const *)title, strlen(title)); } // TODO: Comment or description? strarg_t const username = SLNSessionGetUsername(session); if(username) { yajl_gen_string(json, (unsigned char const *)STR_LEN("submitter-name")); yajl_gen_string(json, (unsigned char const *)username, strlen(username)); } strarg_t const reponame = SLNRepoGetName(blog->repo); if(reponame) { yajl_gen_string(json, (unsigned char const *)STR_LEN("submitter-repo")); yajl_gen_string(json, (unsigned char const *)reponame, strlen(reponame)); } time_t const now = time(NULL); struct tm t[1]; gmtime_r(&now, t); // TODO: Error checking? str_t tstr[31+1]; size_t const tlen = strftime(tstr, sizeof(tstr), "%FT%TZ", t); // ISO 8601 if(tlen) { yajl_gen_string(json, (unsigned char const *)STR_LEN("submission-time")); yajl_gen_string(json, (unsigned char const *)tstr, tlen); } yajl_gen_string(json, (unsigned char const *)STR_LEN("submission-software")); yajl_gen_string(json, (unsigned char const *)STR_LEN("StrongLink Blog")); str_t *fulltext = aasprintf("%s\n%s", title ?: "", NULL ?: ""); // TODO: Description, GNU-ism if(fulltext) { yajl_gen_string(json, (unsigned char const *)STR_LEN("fulltext")); yajl_gen_string(json, (unsigned char const *)fulltext, strlen(fulltext)); } FREE(&fulltext); yajl_gen_map_close(json); rc = SLNSubmissionEnd(extra); if(rc < 0) goto cleanup; SLNSubmissionRef subs[] = { sub, meta, extra }; rc = SLNSubmissionStoreBatch(subs, numberof(subs)); location = aasprintf("/?q=%s", target_QSEscaped); if(!location) rc = UV_ENOMEM; if(rc < 0) goto cleanup; HTTPConnectionSendRedirect(conn, 303, location); cleanup: if(json) { yajl_gen_free(json); json = NULL; } FREE(&title); SLNSubmissionFree(&sub); SLNSubmissionFree(&meta); SLNSubmissionFree(&extra); MultipartFormFree(&form); FREE(&target_QSEscaped); FREE(&location); if(rc < 0) return 500; return 0; }
static str_t *BlogCopyPreviewPath(BlogRef const blog, strarg_t const hash) { return aasprintf("%s/%.2s/%s", blog->cacheDir, hash, hash); }
static int import(SLNPullRef const pull, strarg_t const URI, size_t const pos, HTTPConnectionRef *const conn) { if(!pull) return 0; // TODO: Even if there's nothing to do, we have to enqueue something to fill up our reserved slots. I guess it's better than doing a lot of work inside the connection lock, but there's got to be a better way. SLNSubmissionRef sub = NULL; HTTPHeadersRef headers = NULL; if(!URI) goto enqueue; str_t algo[SLN_ALGO_SIZE]; str_t hash[SLN_HASH_SIZE]; if(SLNParseURI(URI, algo, hash) < 0) goto enqueue; int rc = SLNSessionGetFileInfo(pull->session, URI, NULL); if(rc >= 0) goto enqueue; db_assertf(DB_NOTFOUND == rc, "Database error: %s", sln_strerror(rc)); // TODO: We're logging out of order when we do it like this... // alogf("Pulling %s\n", URI); if(!*conn) { rc = HTTPConnectionCreateOutgoing(pull->host, 0, conn); if(rc < 0) { alogf("Pull import connection error: %s\n", sln_strerror(rc)); goto fail; } } str_t *path = aasprintf("/sln/file/%s/%s", algo, hash); if(!path) { alogf("Pull aasprintf error\n"); goto fail; } rc = HTTPConnectionWriteRequest(*conn, HTTP_GET, path, pull->host); assert(rc >= 0); // TODO FREE(&path); HTTPConnectionWriteHeader(*conn, "Cookie", pull->cookie); HTTPConnectionBeginBody(*conn); rc = HTTPConnectionEnd(*conn); if(rc < 0) { alogf("Pull import request error: %s\n", sln_strerror(rc)); goto fail; } int const status = HTTPConnectionReadResponseStatus(*conn); if(status < 0) { alogf("Pull import response error: %s\n", sln_strerror(status)); goto fail; } if(status < 200 || status >= 300) { alogf("Pull import status error: %d\n", status); goto fail; } rc = HTTPHeadersCreateFromConnection(*conn, &headers); assert(rc >= 0); // TODO /* if(rc < 0) { alogf("Pull import headers error %s\n", sln_strerror(rc)); goto fail; }*/ strarg_t const type = HTTPHeadersGet(headers, "content-type"); rc = SLNSubmissionCreate(pull->session, URI, &sub); if(rc < 0) { alogf("Pull submission error: %s\n", sln_strerror(rc)); goto fail; } rc = SLNSubmissionSetType(sub, type); if(rc < 0) { alogf("Pull submission type error: %s\n", sln_strerror(rc)); goto fail; } for(;;) { if(pull->stop) goto fail; uv_buf_t buf[1] = {}; rc = HTTPConnectionReadBody(*conn, buf); if(rc < 0) { alogf("Pull download error: %s\n", sln_strerror(rc)); goto fail; } if(0 == buf->len) break; rc = SLNSubmissionWrite(sub, (byte_t *)buf->base, buf->len); if(rc < 0) { alogf("Pull write error\n"); goto fail; } } rc = SLNSubmissionEnd(sub); if(rc < 0) { alogf("Pull submission error: %s\n", sln_strerror(rc)); goto fail; } enqueue: HTTPHeadersFree(&headers); async_mutex_lock(pull->mutex); pull->queue[pos] = sub; sub = NULL; pull->filled[pos] = true; async_cond_broadcast(pull->cond); async_mutex_unlock(pull->mutex); return 0; fail: HTTPHeadersFree(&headers); SLNSubmissionFree(&sub); HTTPConnectionFree(conn); return -1; }
void *convert_file(void *arg) { bool header = false; char line[LINE_MAX]; char *name = NULL; char *exec = NULL; char *path = NULL; char *outname = NULL; FILE *file = NULL; path = aasprintf(100,DBUS_DEFAULT_SERVICE_DIR "/%s", (char *)arg); #ifdef THREADS free(arg); #endif file = fopen(path, "r"); if (!file) goto fail;; while (fgets(line, LINE_MAX, file)) { char *val = str_split_one(line, '='); trim(line); if (val) trim(val); switch (line[0]) { case '#': break; case '[': if (streq(line, "[D-BUS Service]")) header = true; else header = false; break; default: if (!header || !val) break; if (streq(line, "Name")) name = strdupa(val); if (streq(line, "Exec")) exec = strdupa(val); break; } if (name && exec) break; } fclose(file); if (!name || !exec) goto fail; outname = aasprintf(250, "%s/dbus-%s.service", systemd_service_dir, name); file = fopen(outname, "w"); if (!file) goto fail; fprintf(file, "# Generated by the systemd-DBus generator\n" "[Unit]\n" "Description=DBUS: %s\n" "SourcePath=%s\n\n" "[Service]\n" "Type=dbus\n" "BusName=%s\n" "ExecStart=%s\n", name, path, name, exec); fclose(file); return NULL; fail: return (void *) 1; }
char *jail_dir(const char *subdir) { return aasprintf("%s/%s", new_root, subdir); }
int is_admin(struct rekey_session *sess) { static int ldap_initialized=0; char *username=NULL; LDAP *l=NULL; int v, ssl_hard=LDAP_OPT_X_TLS_HARD, rc, ret=0; struct timeval tv; LDAPMessage *response=NULL; char *reason, *filter; char *ldap_url, *ldap_base, *ldap_filter, *ldap_binddn; char *ldap_pwfile, *ldap_cacertdir; char ldap_pwbuf[257]; #ifdef HAVE_KRB5_REALM krb5_realm *realm; #else krb5_data rdata; krb5_data *realm = &rdata; #endif #if !defined(LDAP_OPT_X_TLS_PROTOCOL_MIN) SSL_CTX *sslctx; #endif if (!princ_ncomp_eq(sess->kctx, sess->princ, 2) || !compare_princ_comp(sess->kctx, sess->princ, 1, "admin")) { goto freeall; } if (!(username=dup_comp_string(sess->kctx, sess->princ, 0))) { prtmsg("Failed to extract username for admin check"); goto freeall; } #ifdef HAVE_KRB5_REALM realm=sess->realm; #else rdata.data=sess->realm; rdata.length=strlen(sess->realm); #endif krb5_appdefault_string(sess->kctx, "rekey", realm, "ldap_uri", LDAP_URI, &ldap_url); krb5_appdefault_string(sess->kctx, "rekey", realm, "ldap_base", LDAP_BASEDN, &ldap_base); krb5_appdefault_string(sess->kctx, "rekey", realm, "ldap_filter", USER_INGROUP_FILTER, &ldap_filter); krb5_appdefault_string(sess->kctx, "rekey", realm, "ldap_binddn", LDAP_BINDDN, &ldap_binddn); krb5_appdefault_string(sess->kctx, "rekey", realm, "ldap_pwfile", LDAP_PWFILE, &ldap_pwfile); krb5_appdefault_string(sess->kctx, "rekey", realm, "ldap_cacertdir", "/etc/andy/ldapcerts", &ldap_cacertdir); if (strlen(ldap_pwfile) > 0) { int fd=open(ldap_pwfile, O_RDONLY); ssize_t rsize; if (fd < 0) { prtmsg("Failed to open LDAP password file %s: %s", ldap_pwfile, strerror(errno)); goto freeall; } rsize=read(fd, ldap_pwbuf, 256); if (rsize < 0) { prtmsg("Failed to read from LDAP password file %s: %s", ldap_pwfile, strerror(errno)); goto freeall; } if (rsize > 255) { prtmsg("LDAP password file %s is too large. limit to 255 characters", ldap_pwfile); goto freeall; } while(rsize > 0 && isspace(ldap_pwbuf[rsize-1])) rsize--; ldap_pwbuf[rsize]=0; } if (!ldap_initialized) { LDAP_SET_OPTION(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &ssl_hard); LDAP_SET_OPTION(NULL, LDAP_OPT_X_TLS_CACERTDIR, ldap_cacertdir); LDAP_SET_OPTION(NULL, LDAP_OPT_X_TLS_CIPHER_SUITE, "HIGH:!ADH:!eNULL:-SSLv2"); #if defined(LDAP_OPT_X_TLS_PROTOCOL_MIN) v=LDAP_OPT_X_TLS_PROTOCOL_TLS1_0; LDAP_SET_OPTION(NULL, LDAP_OPT_X_TLS_PROTOCOL_MIN, &v); #else extern int ldap_pvt_tls_init(); extern int ldap_pvt_tls_init_def_ctx( int is_server ); ldap_pvt_tls_init(); ldap_pvt_tls_init_def_ctx(0); LDAP_GET_OPTION(NULL, LDAP_OPT_X_TLS_CTX, &sslctx); if (sslctx) { SSL_CTX_set_options(sslctx, SSL_OP_NO_SSLv2); SSL_CTX_set_options(sslctx, SSL_OP_NO_SSLv3); } #endif ldap_initialized=1; } errno=0; rc = ldap_initialize(&l, ldap_url); if (rc!=LDAP_SUCCESS) { prtmsg("Failed to initialize ldap for %s: %s%s%s", ldap_url, ldap_err2string(rc),(errno==0)?"":": ", (errno==0)?"":strerror(errno)); goto freeall; } v=LDAP_VERSION3; LDAP_SET_OPTION(l, LDAP_OPT_PROTOCOL_VERSION, &v); LDAP_SET_OPTION(l, LDAP_OPT_X_TLS, &ssl_hard); errno=0; #if 0 rc = ldap_sasl_interactive_bind_s(l, NULL, "GSSAPI", NULL, NULL, LDAP_SASL_QUIET, do_sasl_interact, NULL); #else rc = ldap_bind_s(l, ldap_binddn, ldap_pwbuf, LDAP_AUTH_SIMPLE); #endif if (rc!=LDAP_SUCCESS) { prtmsg("Failed to connect or authenticate to ldap for %s: %s%s%s", LDAP_URI, ldap_err2string(rc),(errno==0)?"":": ", (errno==0)?"":strerror(errno)); goto freeall; } tv.tv_sec=30; tv.tv_usec=0; rc = ldap_search_ext_s(l, rekey_admin_group, LDAP_SCOPE_BASE, NO_FILTER, no_attrs, 0, NULL, NULL, &tv, LDAP_NO_LIMIT, &response); if (rc != LDAP_SUCCESS) { prtmsg("Failed to verify group %s existence (searching): %s%s%s", rekey_admin_group, ldap_err2string(rc),(errno==0)?"":": ", (errno==0)?"":strerror(errno)); goto freeall; } reason=aasprintf("verify group %s existence", rekey_admin_group); if (!verify_op_success(l, reason, response)) goto freeall; ldap_msgfree(response); response=NULL; filter=aasprintf(ldap_filter, username, rekey_admin_group); rc = ldap_search_ext_s(l, ldap_base, LDAP_SCOPE_SUB, filter, no_attrs, 0, NULL, NULL, &tv, LDAP_NO_LIMIT, &response); if (rc != LDAP_SUCCESS) { prtmsg("Failed to check user %s admin permission (searching): %s%s%s", username, ldap_err2string(rc),(errno==0)?"":": ", (errno==0)?"":strerror(errno)); goto freeall; } reason=aasprintf("check user %s admin permission", username); if (!verify_single_result(l, 0, reason, response)) goto freeall; ret=1; freeall: ldap_msgfree(response); if (l) ldap_unbind_ext_s(l, NULL, NULL); free(username); return ret; }