/** * Load the admin users * * @return Table of users */ static USERS * loadUsers() { USERS *rval; FILE *fp; char fname[1024], *home; char uname[80], passwd[80]; initialise(); snprintf(fname,1023, "%s/passwd", get_datadir()); fname[1023] = '\0'; if ((fp = fopen(fname, "r")) == NULL) { return NULL; } if ((rval = users_alloc()) == NULL) { fclose(fp); return NULL; } while (fscanf(fp, "%[^:]:%s\n", uname, passwd) == 2) { users_add(rval, uname, passwd); } fclose(fp); return rval; }
/** * Allocate a new service for the gateway to support * * * @param servname The service name * @param router Name of the router module this service uses * * @return The newly created service or NULL if an error occured */ SERVICE * service_alloc(char *servname, char *router) { SERVICE *service; if ((service = (SERVICE *)malloc(sizeof(SERVICE))) == NULL) return NULL; if ((service->router = load_module(router, MODULE_ROUTER)) == NULL) { free(service); return NULL; } service->name = strdup(servname); service->routerModule = strdup(router); memset(&service->stats, 0, sizeof(SERVICE_STATS)); service->ports = NULL; service->stats.started = time(0); service->state = SERVICE_STATE_ALLOC; service->credentials.name = NULL; service->credentials.authdata = NULL; service->users = users_alloc(); service->enable_root = 0; service->routerOptions = NULL; service->databases = NULL; spinlock_init(&service->spin); spinlock_acquire(&service_spin); service->next = allServices; allServices = service; spinlock_release(&service_spin); return service; }
/** * Add user * * @param uname Name of the new user * @param passwd Password for the new user * @return NULL on success or an error string on failure */ char * admin_add_user(char *uname, char *passwd) { FILE *fp; char fname[1024], *home, *cpasswd; initialise(); if (access(get_datadir(), F_OK) != 0) { if (mkdir(get_datadir(), S_IRWXU) != 0 && errno != EEXIST) { return ADMIN_ERR_PWDFILEOPEN; } } snprintf(fname,1023, "%s/passwd", get_datadir()); fname[1023] = '\0'; if (users == NULL) { MXS_NOTICE("Create initial password file."); if ((users = users_alloc()) == NULL) { return ADMIN_ERR_NOMEM; } if ((fp = fopen(fname, "w")) == NULL) { MXS_ERROR("Unable to create password file %s.", fname); return ADMIN_ERR_PWDFILEOPEN; } fclose(fp); } if (users_fetch(users, uname) != NULL) { return ADMIN_ERR_DUPLICATE; } struct crypt_data cdata; cdata.initialized = 0; cpasswd = crypt_r(passwd, ADMIN_SALT, &cdata); users_add(users, uname, cpasswd); if ((fp = fopen(fname, "a")) == NULL) { MXS_ERROR("Unable to append to password file %s.", fname); return ADMIN_ERR_FILEAPPEND; } fprintf(fp, "%s:%s\n", uname, cpasswd); fclose(fp); return ADMIN_SUCCESS; }
/** * Add user * * @param uname Name of the new user * @param passwd Password for the new user * @return NULL on success or an error string on failure */ char * admin_add_user(char *uname, char *passwd) { FILE *fp; char fname[1024], *home, *cpasswd; initialise(); sprintf(fname, "%s/passwd", get_datadir()); if (users == NULL) { LOGIF(LM, (skygw_log_write(LOGFILE_MESSAGE, "Create initial password file."))); if ((users = users_alloc()) == NULL) return ADMIN_ERR_NOMEM; if ((fp = fopen(fname, "w")) == NULL) { LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Unable to create password file %s.", fname))); return ADMIN_ERR_PWDFILEOPEN; } fclose(fp); } if (users_fetch(users, uname) != NULL) { return ADMIN_ERR_DUPLICATE; } cpasswd = crypt(passwd, ADMIN_SALT); users_add(users, uname, cpasswd); if ((fp = fopen(fname, "a")) == NULL) { LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, "Error : Unable to append to password file %s.", fname))); return ADMIN_ERR_FILEAPPEND; } fprintf(fp, "%s:%s\n", uname, cpasswd); fclose(fp); return ADMIN_SUCCESS; }
/** * Start an individual port/protocol pair * * @param service The service * @param port The port to start * @return The number of listeners started */ static int serviceStartPort(SERVICE *service, SERV_PROTOCOL *port) { int listeners = 0; char config_bind[40]; GWPROTOCOL *funcs; port->listener = dcb_alloc(DCB_ROLE_SERVICE_LISTENER); if (port->listener == NULL) { return 0; } if (strcmp(port->protocol, "MySQLClient") == 0) { int loaded; /* Allocate specific data for MySQL users */ service->users = mysql_users_alloc(); loaded = load_mysql_users(service); /* At service start last update is set to USERS_REFRESH_TIME seconds earlier. * This way MaxScale could try reloading users' just after startup */ service->rate_limit.last=time(NULL) - USERS_REFRESH_TIME; service->rate_limit.nloads=1; LOGIF(LM, (skygw_log_write( LOGFILE_MESSAGE, "Loaded %d MySQL Users.", loaded))); } else { /* Generic users table */ service->users = users_alloc(); } if ((funcs = (GWPROTOCOL *)load_module(port->protocol, MODULE_PROTOCOL)) == NULL) { dcb_free(port->listener); port->listener = NULL; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Unable to load protocol module %s. Listener " "for service %s not started.", port->protocol, service->name))); return 0; } memcpy(&(port->listener->func), funcs, sizeof(GWPROTOCOL)); port->listener->session = NULL; if (port->address) sprintf(config_bind, "%s:%d", port->address, port->port); else sprintf(config_bind, "0.0.0.0:%d", port->port); if (port->listener->func.listen(port->listener, config_bind)) { port->listener->session = session_alloc(service, port->listener); if (port->listener->session != NULL) { port->listener->session->state = SESSION_STATE_LISTENER; listeners += 1; } else { dcb_close(port->listener); } } else { dcb_close(port->listener); LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Unable to start to listen port %d for %s %s.", port->port, port->protocol, service->name))); } return listeners; }
/** * Start an individual port/protocol pair * * @param service The service * @param port The port to start * @return The number of listeners started */ static int serviceStartPort(SERVICE *service, SERV_PROTOCOL *port) { int listeners = 0; char config_bind[40]; GWPROTOCOL *funcs; port->listener = dcb_alloc(DCB_ROLE_SERVICE_LISTENER); if (port->listener == NULL) { LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Failed to create listener for service %s.", service->name))); goto retblock; } if (strcmp(port->protocol, "MySQLClient") == 0) { int loaded; if (service->users == NULL) { /* * Allocate specific data for MySQL users * including hosts and db names */ service->users = mysql_users_alloc(); if ((loaded = load_mysql_users(service)) < 0) { LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Unable to load users from %s:%d for " "service %s.", (port->address == NULL ? "0.0.0.0" : port->address), port->port, service->name))); { /* Try loading authentication data from file cache */ char *ptr, path[4097]; strcpy(path, "/usr/local/mariadb-maxscale"); if ((ptr = getenv("MAXSCALE_HOME")) != NULL) { strncpy(path, ptr, 4096); } strncat(path, "/", 4096); strncat(path, service->name, 4096); strncat(path, "/.cache/dbusers", 4096); loaded = dbusers_load(service->users, path); if (loaded != -1) { LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Using cached credential information."))); } } if (loaded == -1) { hashtable_free(service->users->data); free(service->users); dcb_free(port->listener); port->listener = NULL; goto retblock; } } else { /* Save authentication data to file cache */ char *ptr, path[4097]; int mkdir_rval = 0; strcpy(path, "/usr/local/mariadb-maxscale"); if ((ptr = getenv("MAXSCALE_HOME")) != NULL) { strncpy(path, ptr, 4096); } strncat(path, "/", 4096); strncat(path, service->name, 4096); if (access(path, R_OK) == -1) { mkdir_rval = mkdir(path, 0777); } if(mkdir_rval) { skygw_log_write(LOGFILE_ERROR,"Error : Failed to create directory '%s': [%d] %s", path, errno, strerror(errno)); mkdir_rval = 0; } strncat(path, "/.cache", 4096); if (access(path, R_OK) == -1) { mkdir_rval = mkdir(path, 0777); } if(mkdir_rval) { skygw_log_write(LOGFILE_ERROR,"Error : Failed to create directory '%s': [%d] %s", path, errno, strerror(errno)); mkdir_rval = 0; } strncat(path, "/dbusers", 4096); dbusers_save(service->users, path); } if (loaded == 0) { LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Service %s: failed to load any user " "information. Authentication will " "probably fail as a result.", service->name))); } /* At service start last update is set to USERS_REFRESH_TIME seconds earlier. * This way MaxScale could try reloading users' just after startup */ service->rate_limit.last=time(NULL) - USERS_REFRESH_TIME; service->rate_limit.nloads=1; LOGIF(LM, (skygw_log_write( LOGFILE_MESSAGE, "Loaded %d MySQL Users for service [%s].", loaded, service->name))); } } else { if (service->users == NULL) { /* Generic users table */ service->users = users_alloc(); } } if ((funcs=(GWPROTOCOL *)load_module(port->protocol, MODULE_PROTOCOL)) == NULL) { if (service->users->data) { hashtable_free(service->users->data); } free(service->users); dcb_free(port->listener); port->listener = NULL; LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Unable to load protocol module %s. Listener " "for service %s not started.", port->protocol, service->name))); goto retblock; } memcpy(&(port->listener->func), funcs, sizeof(GWPROTOCOL)); port->listener->session = NULL; if (port->address) sprintf(config_bind, "%s:%d", port->address, port->port); else sprintf(config_bind, "0.0.0.0:%d", port->port); if (port->listener->func.listen(port->listener, config_bind)) { port->listener->session = session_alloc(service, port->listener); if (port->listener->session != NULL) { port->listener->session->state = SESSION_STATE_LISTENER; listeners += 1; } else { LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Failed to create session to service %s.", service->name))); if (service->users->data) { hashtable_free(service->users->data); } free(service->users); dcb_close(port->listener); port->listener = NULL; goto retblock; } } else { LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Unable to start to listen port %d for %s %s.", port->port, port->protocol, service->name))); if (service->users->data) { hashtable_free(service->users->data); } free(service->users); dcb_close(port->listener); port->listener = NULL; } retblock: return listeners; }