Sun, 26 May 2013 12:12:07 +0200
added authentication cache
/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2013 Olaf Wintermann. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "../public/nsapi.h" #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <sys/types.h> #include <sys/file.h> #include <sys/stat.h> #include <sys/mman.h> #include "../ucx/string.h" #include "httplistener.h" #include "config.h" #include "func.h" #include "log.h" #include "event.h" #include "threadpools.h" #include "ldap_auth.h" #include "configmanager.h" #include "vserver.h" #include "../util/pblock.h" #include "../util/util.h" #include "../util/atomic.h" pool_handle_t *cfg_pool; // TODO: Funktion für ConfigDirective -> directive // TODO: Funktion für UcxList parameter list -> pblock int load_init_conf(char *file) { printf("load_init_conf\n"); InitConfig *cfg = load_init_config(file); UcxMempool *mp = cfg->parser.mp; if(cfg == NULL) { return 1; } cfg_pool = pool_create(); // one pool for one Configuration UcxDlist *dirs = cfg->directives; while(dirs != NULL) { ConfigDirective *dir = dirs->data; /* create NSAPI directive */ directive *d = malloc(sizeof(directive)); d->param = pblock_create_pool(cfg_pool, 8); UcxList *param = cfg_param_list(dir->value, mp); while(param != NULL) { ConfigParam *p = param->data; pblock_nvlinsert( p->name.ptr, p->name.length, p->value.ptr, p->value.length, d->param); param = param->next; } /* get function */ char *func_name = pblock_findval("fn", d->param); d->func = get_function(func_name); if(d->func == NULL) { free(d); pblock_free(d->param); //dirs = dirs->next; log_ereport( LOG_MISCONFIG, "Cannot find Init function %s", func_name); return 1; } /* execute init directive */ int ret = d->func->func(d->param, NULL, NULL); if(ret != REQ_PROCEED && ret != REQ_NOACTION) { log_ereport( LOG_FAILURE, "Error running Init function %s", func_name); return 1; } dirs = dirs->next; } free_init_config(cfg); return 0; } ServerConfiguration* load_server_conf(ServerConfiguration *old, char *file) { printf("load_server_conf\n"); ServerConfig *serverconf = load_server_config(file); if(serverconf == NULL) { fprintf(stderr, "Cannot load server.conf\n"); } ServerConfiguration *serverconfig = calloc(1, sizeof(ServerConfiguration)); serverconfig->ref = 1; serverconfig->pool = pool_create(); serverconfig->listeners = NULL; serverconfig->host_vs = ucx_map_new(16); serverconfig->authdbs = ucx_map_new(16); // TODO: init serverconfig stuff /* * convert ServerConfig to ServerConfiguration * * its important to do this in the correct order: * LogFile (open log file first to log possible errors) * Runtime * Threadpool * EventHandler * AuthDB * Listener (we set the VirtualServer later) * VirtualServer (dependencies: Listener) */ /* init logfile first */ UcxList *lfl = ucx_map_sstr_get(serverconf->objects, sstrn("LogFile", 7)); if(lfl != NULL) { ServerConfigObject *logobj = lfl->data; if(logobj == NULL) { /* error */ return NULL; } int ret = cfg_handle_logfile(serverconfig, logobj); if(ret != 0) { /* cannot initialize log file */ return NULL; } } else { /* horrible error */ return NULL; } UcxList *list = ucx_map_sstr_get(serverconf->objects, sstrn("Runtime", 7)); UCX_FOREACH(UcxList*, list, elm) { ServerConfigObject *scfgobj = elm->data; cfg_handle_runtime(serverconfig, scfgobj); } list = ucx_map_sstr_get(serverconf->objects, sstrn("Threadpool", 10)); UCX_FOREACH(UcxList*, list, elm) { cfg_handle_threadpool(serverconfig, elm->data); } // check thread pool config if(check_thread_pool_cfg() != 0) { /* critical error */ return NULL; } list = ucx_map_sstr_get(serverconf->objects, sstrn("EventHandler", 12)); UCX_FOREACH(UcxList*, list, elm) { cfg_handle_eventhandler(serverconfig, (ServerConfigObject*)elm->data); } // check event handler config if(check_event_handler_cfg() != 0) { /* critical error */ return NULL; } list = ucx_map_sstr_get(serverconf->objects, sstrn("AccessLog", 9)); UCX_FOREACH(UcxList*, list, elm) { ServerConfigObject *scfgobj = elm->data; cfg_handle_accesslog(serverconfig, scfgobj); } list = ucx_map_sstr_get(serverconf->objects, sstrn("AuthDB", 6)); UCX_FOREACH(UcxList*, list, elm) { ServerConfigObject *scfgobj = elm->data; cfg_handle_authdb(serverconfig, scfgobj); } list = ucx_map_sstr_get(serverconf->objects, sstrn("Listener", 8)); UCX_FOREACH(UcxList*, list, elm) { ServerConfigObject *scfgobj = elm->data; cfg_handle_listener(serverconfig, scfgobj); } list = ucx_map_sstr_get(serverconf->objects, sstrn("VirtualServer", 13)); UCX_FOREACH(UcxList*, list, elm) { ServerConfigObject *scfgobj = elm->data; cfg_handle_vs(serverconfig, scfgobj); } // set VirtualServer for all listeners UcxList *ls = serverconfig->listeners; while(ls) { HttpListener *listener = ls->data; sstr_t vsname = sstr(listener->default_vs.vs_name); // search for VirtualServer //int b = 0; UcxMapIterator iter = ucx_map_iterator(serverconfig->host_vs); VirtualServer *vs; UCX_MAP_FOREACH(vs, iter) { if(!sstrcmp(vsname, vs->name)) { listener->default_vs.vs = vs; break; } } ls = ls->next; } return serverconfig; } void cfg_ref(ServerConfiguration *cfg) { ws_atomic_inc32(&cfg->ref); } void cfg_unref(ServerConfiguration *cfg) { uint32_t ref = ws_atomic_dec32(&cfg->ref); if(ref == 0) { // TODO: free configuration printf("free ServerConfiguration %d\n", cfg); } } void init_server_config_parser() { } int cfg_handle_runtime(ServerConfiguration *cfg, ServerConfigObject *obj) { cfg->user = sstrdup(cfg_directivelist_get_str( obj->directives, sstr("User"))); cfg->tmp = sstrdup(cfg_directivelist_get_str( obj->directives, sstr("Temp"))); // mime file sstr_t mf = cfg_directivelist_get_str(obj->directives, sstr("MimeFile")); sstr_t base = sstr("config/"); sstr_t file; file.length = base.length + mf.length; file.ptr = alloca(file.length + 1); file.ptr[file.length] = 0; file = sstrncat(2, file, base, mf); ConfigFile *f = cfgmgr_get_file(file); if(f == NULL) { f = malloc(sizeof(ConfigFile)); f->data = NULL; f->file = sstrdup(file); f->reload = mime_conf_reload; // load the file content //f->reload(f, cfg); if(cfgmgr_reload_file(f, cfg, NULL)) { free(f->file.ptr); free(f); return -1; } cfgmgr_attach_file(f); } cfg->mimetypes = f->data; return 0; } int cfg_handle_logfile(ServerConfiguration *cfg, ServerConfigObject *obj) { sstr_t file = cfg_directivelist_get_str(obj->directives, sstr("File")); sstr_t lvl = cfg_directivelist_get_str(obj->directives, sstr("Level")); if(file.ptr == NULL || lvl.ptr == NULL) { /* missing log file parameters */ return -1; } LogConfig logcfg; logcfg.file = sstrdup(file).ptr; logcfg.level = sstrdup(lvl).ptr; /* TODO: stdout, stderr config */ int ret = init_log_file(&logcfg); free(logcfg.file); free(logcfg.level); return ret; } int cfg_handle_threadpool(ServerConfiguration *cfg, ServerConfigObject *obj) { ThreadPoolConfig poolcfg; poolcfg.min_threads = 4; poolcfg.min_threads = 4; poolcfg.max_threads = 8; poolcfg.queue_size = 64; poolcfg.stack_size = 262144; sstr_t name = cfg_directivelist_get_str( obj->directives, sstr("Name")); sstr_t min = cfg_directivelist_get_str( obj->directives, sstr("MinThreads")); sstr_t max = cfg_directivelist_get_str( obj->directives, sstr("MaxThreads")); sstr_t stack = cfg_directivelist_get_str( obj->directives, sstr("StackSize")); sstr_t queue = cfg_directivelist_get_str( obj->directives, sstr("QueueSize")); // TODO: Type if(name.length == 0) { // TODO: log error return 1; } if(min.length != 0) { min = sstrdup(min); poolcfg.min_threads = atoi(min.ptr); free(min.ptr); } if(max.length != 0) { max = sstrdup(max); poolcfg.max_threads = atoi(max.ptr); free(max.ptr); } if(stack.length != 0) { stack = sstrdup(stack); poolcfg.stack_size = atoi(stack.ptr); free(stack.ptr); } if(queue.length != 0) { queue = sstrdup(queue); poolcfg.queue_size = atoi(queue.ptr); free(queue.ptr); } create_threadpool(name, &poolcfg); } int cfg_handle_eventhandler(ServerConfiguration *c, ServerConfigObject *obj) { EventHandlerConfig evcfg; sstr_t name = cfg_directivelist_get_str(obj->directives, sstr("Name")); sstr_t threads = cfg_directivelist_get_str( obj->directives, sstr("Threads")); sstr_t isdefault = cfg_directivelist_get_str( obj->directives, sstr("Default")); evcfg.name = name; sstr_t s = sstrdup(threads); evcfg.nthreads = atoi(s.ptr); free(s.ptr); evcfg.isdefault = util_getboolean(isdefault.ptr, 0); int ret = create_event_handler(&evcfg); return ret; } int cfg_handle_accesslog(ServerConfiguration *cfg, ServerConfigObject *obj) { // TODO: use a name to identify the log file sstr_t file = cfg_directivelist_get_str(obj->directives, sstr("File")); if(file.ptr == NULL) { return 0; } sstr_t format; format.ptr = NULL; AccessLog *log = get_access_log(file, format); if(log) { // access logs are only stored in the server config to free them cfg->logfiles = ucx_list_append(cfg->logfiles, log); } return 0; } int cfg_handle_authdb(ServerConfiguration *cfg, ServerConfigObject *obj) { sstr_t name = cfg_directivelist_get_str(obj->directives, sstr("Name")); sstr_t type = cfg_directivelist_get_str(obj->directives, sstr("Type")); if(!sstrcmp(type, sstr("ldap"))) { LDAPConfig conf; sstr_t host = cfg_directivelist_get_str( obj->directives, sstr("Host")); sstr_t port = cfg_directivelist_get_str( obj->directives, sstr("Port")); sstr_t basedn = cfg_directivelist_get_str( obj->directives, sstr("BaseDN")); sstr_t binddn = cfg_directivelist_get_str( obj->directives, sstr("BindDN")); sstr_t basepw = cfg_directivelist_get_str( obj->directives, sstr("BindPW")); host = sstrdup(host); port = sstrdup(port); basedn = sstrdup(basedn); binddn = sstrdup(binddn); basepw = sstrdup(basepw); conf.hostname = host.ptr; conf.port = atoi(port.ptr); conf.basedn = basedn.ptr; conf.binddn = binddn.ptr; conf.bindpw = basepw.ptr; name = sstrdup(name); AuthDB *authdb = create_ldap_authdb(name.ptr, &conf); printf("authdb: %d\n", authdb); ucx_map_sstr_put(cfg->authdbs, name, authdb); // TODO: create_ldap_authdb should copy the strings /* free(host.ptr); free(port.ptr); free(basedn.ptr); free(binddn.ptr); free(basepw.ptr); free(name.ptr); */ } else if(!sstrcmp(type, sstr("keyfile"))) { // we only need the file parameter sstr_t file = cfg_directivelist_get_str( obj->directives, sstr("File")); if(file.length == 0) { log_ereport( LOG_MISCONFIG, "missing File parameter for keyfile authdb"); return 1; } // load keyfile ConfigFile *f = cfgmgr_get_file(file); if(f == NULL) { f = malloc(sizeof(ConfigFile)); f->data = NULL; f->file = sstrdup(file); f->reload = keyfile_reload; //f->reload(f, cfg); if(cfgmgr_reload_file(f, cfg, NULL)) { free(f->file.ptr); free(f); return -1; } cfgmgr_attach_file(f); } // add keyfile authdb Keyfile *keyfile = f->data; keyfile->authdb.name = sstrdup(name).ptr; printf("authdb: %d\n", keyfile); ucx_map_sstr_put(cfg->authdbs, name, keyfile); } return 0; } int cfg_handle_listener(ServerConfiguration *cfg, ServerConfigObject *obj) { ListenerConfig lc; lc.cfg = cfg; lc.port = 8080; lc.nacceptors = 1; // TODO: use sstrdup_pool? lc.name = sstrdup(cfg_directivelist_get_str( obj->directives, sstr("Name"))); lc.port = atoi(cfg_directivelist_get_str( obj->directives, sstr("Port")).ptr); lc.vs = sstrdup(cfg_directivelist_get_str( obj->directives, sstr("DefaultVS"))); lc.threadpool = sstrdup(cfg_directivelist_get_str( obj->directives, sstr("Threadpool"))); // TODO: check if all important configs are set HttpListener *listener = http_listener_create(&lc); listener->default_vs.vs_name = lc.vs.ptr; cfg->listeners = ucx_list_append(cfg->listeners, listener); return 0; } int cfg_handle_vs(ServerConfiguration *cfg, ServerConfigObject *obj) { VirtualServer *vs = vs_new(); vs->name = sstrdup(cfg_directivelist_get_str( obj->directives, sstr("Name"))); vs->host = sstrdup(cfg_directivelist_get_str( obj->directives, sstr("Host"))); vs->document_root = sstrdup(cfg_directivelist_get_str( obj->directives, sstr("DocRoot"))); sstr_t objfile = cfg_directivelist_get_str( obj->directives, sstr("ObjectFile")); sstr_t aclfile = cfg_directivelist_get_str( obj->directives, sstr("ACLFile")); // load the object config file sstr_t base = sstr("config/"); sstr_t file; file.length = base.length + objfile.length + 1; file.ptr = alloca(file.length); file.ptr[file.length] = 0; file = sstrncat(2, file, base, objfile); // the file is managed by the configuration manager ConfigFile *f = cfgmgr_get_file(file); if(f == NULL) { f = malloc(sizeof(ConfigFile)); f->data = NULL; f->file = sstrdup(file); f->reload = object_conf_reload; //f->reload(f, cfg); if(cfgmgr_reload_file(f, cfg, NULL)) { free(f->file.ptr); free(f); return -1; } cfgmgr_attach_file(f); } vs->objectfile = sstrdup(file); vs->objects = (HTTPObjectConfig*)f->data; // load acl config file file.length = base.length + aclfile.length + 1; file.ptr = alloca(file.length); file.ptr[file.length] = 0; file = sstrncat(2, file, base, aclfile); ConfigFile *aclf = cfgmgr_get_file(file); if(aclf == NULL) { aclf = malloc(sizeof(ConfigFile)); aclf->data = NULL; aclf->file = sstrdup(file); aclf->reload = acl_conf_reload; //aclf->reload(aclf, cfg); if(cfgmgr_reload_file(aclf, cfg, NULL)) { free(aclf->file.ptr); free(aclf); return -1; } cfgmgr_attach_file(aclf); } vs->acls = aclf->data; // set the access log for the virtual server // TODO: don't use always the default vs->log = get_default_access_log(); ucx_map_sstr_put(cfg->host_vs, vs->host, vs); return 0; } int object_conf_reload(ConfigFile *file, ServerConfiguration *cfg) { HTTPObjectConfig *old_conf = file->data; file->data = load_obj_conf(file->file.ptr); if(old_conf) { object_conf_unref(old_conf); } if(file->data) { return 0; } else { return 1; } } void object_conf_ref(HTTPObjectConfig *conf) { if(conf) { ws_atomic_inc32(&conf->ref); } } void object_conf_unref(HTTPObjectConfig *conf) { uint32_t ref = ws_atomic_dec32(&conf->ref); if(ref == 0) { printf("free HTTPObjectConfig %d\n", conf); pool_destroy(conf->pool); } } HTTPObjectConfig* load_obj_conf(char *file) { printf("load_obj_conf\n"); // new conf function test ObjectConfig *cfg = load_object_config(file); UcxMempool *mp = cfg->parser.mp; if(cfg == NULL) { return NULL; } /* create object config */ pool_handle_t *pool = pool_create(); HTTPObjectConfig *conf = pool_calloc(pool, sizeof(HTTPObjectConfig), 1); conf->pool = pool; /* convert ObjectConfig to HTTPObjectConfig */ /* add objects */ conf->nobj = ucx_dlist_size(cfg->objects); conf->objects = pool_calloc(pool, 1, sizeof(httpd_object*)); UcxDlist *objlist = cfg->objects; int i = 0; while(objlist != NULL) { ConfigObject *cob = objlist->data; /* get name and ppath */ char *name = NULL; char *ppath = NULL; if(cob->name.length > 0) { name = sstrdup_pool(pool, cob->name).ptr; } if(cob->ppath.length > 0) { ppath = sstrdup_pool(pool, cob->ppath).ptr; } /* create and add object */ httpd_object *obj = object_new(pool, name); obj->path = NULL; conf->objects[i] = obj; // TODO: beyond array bounds write /* add directives */ for(int i=0;i<6;i++) { UcxDlist *dirs = cob->directives[i]; while(dirs != NULL) { ConfigDirective *cfgdir = dirs->data; directive *d = pool_malloc(pool, sizeof(directive)); d->cond = NULL; d->param = pblock_create_pool(pool, 8); /* add params */ UcxList *param = cfg_param_list(cfgdir->value, mp); while(param != NULL) { ConfigParam *p = param->data; pblock_nvlinsert( p->name.ptr, p->name.length, p->value.ptr, p->value.length, d->param); param = param->next; } /* get function */ char *func_name = pblock_findval("fn", d->param); d->func = get_function(func_name); dirs = dirs->next; /* add function to dtable */ object_add_directive(obj, d, cfgdir->type_num); } } /* next */ i++; objlist = objlist->next; } free_object_config(cfg); return conf; } int mime_conf_reload(ConfigFile *file, ServerConfiguration *cfg) { MimeConfig *mimecfg = load_mime_config(file->file.ptr); MimeMap *old_conf = file->data; MimeMap *mimemap = malloc(sizeof(MimeMap)); mimemap->ref = 1; UcxMap *map = ucx_map_new((mimecfg->ntypes * 3) / 2); mimemap->map = map; // add ext type pairs UCX_FOREACH(UcxList*, mimecfg->directives, md) { MimeDirective *d = md->data; // add the type for each extension to the map UCX_FOREACH(UcxList*, d->exts, xl) { sstr_t ext = sstr(xl->data); sstr_t value = sstrdup(d->type); ucx_map_sstr_put(map, ext, value.ptr); } } file->data = mimemap; if(old_conf) { mime_conf_unref(old_conf); } return 0; } void mime_conf_ref(MimeMap *conf) { if(conf) { ws_atomic_inc32(&conf->ref); } } void mime_conf_unref(MimeMap *conf) { uint32_t ref = ws_atomic_dec32(&conf->ref); if(ref == 0) { printf("free MimeConfig %d\n", conf); UcxMapIterator i = ucx_map_iterator(conf->map); char *str; UCX_MAP_FOREACH(str, i) { free(str); } ucx_map_free(conf->map); free(conf); } } int acl_conf_reload(ConfigFile *file, ServerConfiguration *cfg) { ACLFile *aclfile = load_acl_file(file->file.ptr); ACLData *acldata = acl_data_new(); UCX_FOREACH(UcxList*, aclfile->namedACLs, elm) { ACLConfig *ac = elm->data; ACLList *acl = acl_config_convert(cfg, ac); printf("put acl: %s\n", ac->id.ptr); ucx_map_sstr_put(acldata->namedACLs, ac->id, acl); } free_acl_file(aclfile); ACLData *old_data = file->data; file->data = acldata; if(old_data) { acl_data_unref(old_data); } return 0; } ACLList* acl_config_convert(ServerConfiguration *cfg, ACLConfig *acl) { WSAcl *acllist = malloc(sizeof(WSAcl)); acllist->acl.check = (acl_check_f)wsacl_check; acllist->acl.authdb = NULL; acllist->acl.authprompt = NULL; acllist->acl.isextern = 0; acllist->ace = NULL; acllist->ece = NULL; if(acl->type.ptr && !sstrcmp(acl->type, sstr("fs"))) { printf("set acl to extern\n"); acllist->acl.isextern = 1; } size_t s = ucx_list_size(acl->entries); WSAce **aces = calloc(s, sizeof(WSAce*)); WSAce **eces = calloc(s, sizeof(WSAce*)); int ai = 0; int ei = 0; // convert entries UCX_FOREACH(UcxList*, acl->entries, elm) { ACEConfig *acecfg = elm->data; // copy data WSAce *ace = malloc(sizeof(WSAce)); ace->access_mask = acecfg->access_mask; ace->flags = acecfg->flags; ace->type = acecfg->type; ace->who = sstrdup(acecfg->who).ptr; // add the entry to the correct array if(ace->type >= ACL_TYPE_AUDIT) { eces[ei] = ace; ei++; } else { aces[ai] = ace; ai++; } } // create new entrie arrays with perfect fitting size if(ai > 0) { acllist->ace = calloc(ai, sizeof(WSAce*)); } if(ei > 0) { acllist->ece = calloc(ei, sizeof(WSAce*)); } memcpy(acllist->ace, aces, ai*sizeof(WSAce*)); memcpy(acllist->ece, eces, ei*sizeof(WSAce*)); acllist->acenum = ai; acllist->ecenum = ei; free(aces); free(eces); // get authentication information if(acl->authparam) { sstr_t authdb_str = cfg_param_get(acl->authparam, sstr("authdb")); sstr_t prompt_str = cfg_param_get(acl->authparam, sstr("prompt")); if(authdb_str.ptr) { AuthDB *authdb = ucx_map_sstr_get(cfg->authdbs, authdb_str); acllist->acl.authdb = authdb; if(authdb && prompt_str.ptr) { acllist->acl.authprompt = sstrdup(prompt_str).ptr; } } } return &acllist->acl; } int keyfile_reload(ConfigFile *file, ServerConfiguration *cfg) { KeyfileConfig *conf = load_keyfile_config(file->file.ptr); if(!conf) { return 1; } Keyfile *keyfile = keyfile_new(); UCX_FOREACH(UcxList*, conf->users, elm) { KeyfileEntry *user = elm->data; keyfile_add_user( keyfile, user->name, user->hashtype, user->hashdata, user->groups, user->numgroups); } free_keyfile_config(conf); Keyfile *old_data = file->data; file->data = keyfile; if(old_data) { keyfile_unref(old_data); } return 0; }