Esempio n. 1
0
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);
}
Esempio n. 2
0
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;
}
Esempio n. 3
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);
}
Esempio n. 4
0
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);
}
Esempio n. 5
0
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;
}
Esempio n. 6
0
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;
}
Esempio n. 7
0
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;
}
Esempio n. 8
0
static str_t *BlogCopyPreviewPath(BlogRef const blog, strarg_t const hash) {
	return aasprintf("%s/%.2s/%s", blog->cacheDir, hash, hash);
}
Esempio n. 9
0
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;
}
Esempio n. 10
0
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;
}
Esempio n. 11
0
char *jail_dir(const char *subdir) {
  return aasprintf("%s/%s", new_root, subdir);
}
Esempio n. 12
0
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;
}