#include #include #include #include #include #include #include #include #include #include #include #include #include #if !defined(OS_ARCH) && defined(HAVE_SYSINFO) && defined(HAVE_SYS_SYSTEMINFO_H) #include #endif #include #include "input-method.h" #include "role-data-client.h" #define NODE_DEFAULT "localhost" #define NODE_KEY "node=" #define NODE_KEY_LEN (5) #define SERVICE_DEFAULT "9010" #define SERVICE_KEY "service=" #define SERVICE_KEY_LEN (8) #define SERVER_FILE ".iiim/server" #define SERVER_FILE_LEN (12) #define SERVER_COMPAT_FILE ".iiimp" #define SERVER_COMPAT_FILE_LEN (6) #define SERVER_COMPAT_KEY "iiimp.server=iiimp://" #define SERVER_COMPAT_KEY_LEN (21) #define CONFIG_DIR_BASE ".iiim" #define CONFIG_DIR_AUTH ".iiim/auth" #define CONFIG_FILE_PASSWD ".iiim/auth/passwd" #define PASSWORD_FILE_LEN (17) #define AUTH_PASSWORD_LEN (32) #define HOME_ENV "HOME" #define IIIM_SERVER_ENV "IIIM_SERVER" #define CLIENT_TYPE "generic IIIMP client" const char * iiimf_role_data_client_user_name(IIIMF_im * im) { return im->role_data.client->user_name; } const char * iiimf_role_data_client_home_dir(IIIMF_im * im) { return im->role_data.client->home_dir; } const char * iiimf_role_data_client_node(IIIMF_im * im) { return im->role_data.client->node; } const char * iiimf_role_data_client_server_node(IIIMF_im * im) { return im->role_data.client->server_node; } const char * iiimf_role_data_client_service(IIIMF_im * im) { return im->role_data.client->service; } #define POSITION_HEAD(p, n) \ for (; 0 < n; --(n), (p)++) { \ if ((' ' != *(p)) && ('\t' != *(p)) && \ ('\r' != *(p)) && ('\n' != *(p))) { \ break; \ } \ } \ if ((0 < (n)) && ((',' == *(p)) || (';' == *(p)))) { \ --(n); \ (p)++; \ } \ for (; 0 < n; --(n), (p)++) { \ if ((' ' != *(p)) && ('\t' != *(p)) && \ ('\r' != *(p)) && ('\n' != *(p))) { \ break; \ } \ } #define POSITION_TAIL(p, n) \ for (; 0 < n; --(n), (p)++) { \ if ((',' == *(p)) || (';' == *(p)) || \ (' ' == *(p)) || ('\t' == *(p)) || \ ('\r' == *(p)) || ('\n' == *(p))) { \ break; \ } \ } #define POSITION_TAIL_COMPAT(p, n) \ for (; 0 < n; --(n), (p)++) { \ if ((':' == *(p)) || ('\r' == *(p)) || ('\n' == *(p))) { \ break; \ } \ } static IIIMF_status get_param( const char ** buf, size_t * nbyte, const char * key, size_t key_len, char ** param_ret) { const char * b; const char * p; size_t n; size_t m; size_t len; char * param; b = *buf; n = *nbyte; if (n < key_len) return IIIMF_STATUS_CONFIG; if (0 != strncasecmp(b, key, key_len)) { return IIIMF_STATUS_CONFIG; } b += key_len; n -= key_len; p = b; m = n; POSITION_TAIL(b, n); len = (m - n); param = (char *)malloc(len + 1); if (NULL == param) return IIIMF_STATUS_MALLOC; (void)memcpy(param, p, len); *(param + len) = '\0'; *buf = b; *nbyte = n; *param_ret = param; return IIIMF_STATUS_SUCCESS; } static IIIMF_status get_param_compat( const char ** buf, size_t * nbyte, const char * key, size_t key_len, char ** param_ret) { const char * b; const char * p; size_t n; size_t m; size_t len; char * param; b = *buf; n = *nbyte; if (n < key_len) return IIIMF_STATUS_CONFIG; if (0 != strncasecmp(b, key, key_len)) { return IIIMF_STATUS_CONFIG; } b += key_len; n -= key_len; p = b; m = n; POSITION_TAIL_COMPAT(b, n); len = (m - n); param = (char *)malloc(len + 1); if (NULL == param) return IIIMF_STATUS_MALLOC; (void)memcpy(param, p, len); *(param + len) = '\0'; *buf = b; *nbyte = n; *param_ret = param; return IIIMF_STATUS_SUCCESS; } static IIIMF_status iiimf_role_data_client_file_server(IIIMF_role_data_client * client) { int fd; IIIMF_status status; char * server_file; size_t server_file_len; size_t home_dir_len; struct stat st; int stat_ret; char * pa; const char * p; size_t rest; char * node; char * service; home_dir_len = strlen(client->home_dir); server_file_len = (home_dir_len + 1 + SERVER_FILE_LEN); server_file = (char *)malloc(server_file_len + 1); if (NULL == server_file) { return IIIMF_STATUS_MALLOC; } (void)strcpy(server_file, client->home_dir); *(server_file + home_dir_len) = '/'; (void)strcpy(server_file + home_dir_len + 1, SERVER_FILE); fd = open(server_file, O_RDONLY, 0); free(server_file); if (fd < 0) return IIIMF_STATUS_CONFIG; stat_ret = fstat(fd, &st); if (stat_ret < 0) { return IIIMF_STATUS_CONFIG; } pa = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); (void)close(fd); if (NULL == pa) return IIIMF_STATUS_CONFIG; p = pa; rest = st.st_size; POSITION_HEAD(p, rest); if (rest < NODE_KEY_LEN) { (void)munmap(pa, st.st_size); return IIIMF_STATUS_CONFIG; } status = get_param(&p, &rest, NODE_KEY, NODE_KEY_LEN, &node); if (IIIMF_STATUS_SUCCESS != status) { (void)munmap(pa, st.st_size); return status; } client->server_node = node; POSITION_HEAD(p, rest); if (rest < SERVICE_KEY_LEN) { (void)munmap(pa, st.st_size); return IIIMF_STATUS_SUCCESS; } status = get_param(&p, &rest, SERVICE_KEY, SERVICE_KEY_LEN, &service); (void)munmap(pa, st.st_size); client->service = service; return IIIMF_STATUS_SUCCESS; } static IIIMF_status iiimf_role_data_client_file_compat_server(IIIMF_role_data_client * client) { int fd; IIIMF_status status; char * server_file; size_t server_file_len; size_t home_dir_len; struct stat st; int stat_ret; char * pa; const char * p; size_t rest; char * node; char * service; home_dir_len = strlen(client->home_dir); server_file_len = (home_dir_len + 1 + SERVER_COMPAT_FILE_LEN); server_file = (char *)malloc(server_file_len + 1); if (NULL == server_file) { return IIIMF_STATUS_MALLOC; } (void)strcpy(server_file, client->home_dir); *(server_file + home_dir_len) = '/'; (void)strcpy(server_file + home_dir_len + 1, SERVER_COMPAT_FILE); fd = open(server_file, O_RDONLY, 0); free(server_file); if (fd < 0) return IIIMF_STATUS_CONFIG; stat_ret = fstat(fd, &st); if (stat_ret < 0) { return IIIMF_STATUS_CONFIG; } pa = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); (void)close(fd); if (NULL == pa) return IIIMF_STATUS_CONFIG; p = pa; rest = st.st_size; while (0 < rest) { for (; 0 < rest; --rest, p++) { if (('\r' == *p) || ('\n' == *p)) { p += 1; rest -= 1; break; } } if (rest < SERVER_COMPAT_KEY_LEN) break; status = get_param_compat(&p, &rest, SERVER_COMPAT_KEY, SERVER_COMPAT_KEY_LEN, &node); if (IIIMF_STATUS_SUCCESS == status) { client->server_node = node; p += 1; rest -= 1; break; } } if (NULL == client->server_node) { return IIIMF_STATUS_CONFIG; } status = get_param_compat(&p, &rest, "", 0, &service); (void)munmap(pa, st.st_size); if ('\0' == *service) { free(service); service = NULL; } if (IIIMF_STATUS_SUCCESS == status) { client->service = service; } return status; } static IIIMF_status iiimf_role_data_client_environ_server(IIIMF_role_data_client * client) { IIIMF_status status; const char * p; size_t rest; char * node; char * service; p = getenv(IIIM_SERVER_ENV); if (NULL == p) return IIIMF_STATUS_FAIL; rest = strlen(p); status = get_param(&p, &rest, "", 0, &node); if (IIIMF_STATUS_SUCCESS != status) { return status; } client->server_node = node; POSITION_HEAD(p, rest); status = get_param(&p, &rest, "", 0, &service); client->service = service; return IIIMF_STATUS_SUCCESS; } IIIMF_status iiimf_im_client_type_set( IIIMF_im * im, const char * type) { char * p; p = strdup(type); if (NULL == p) return IIIMF_STATUS_MALLOC; free(im->role_data.client->type); im->role_data.client->type = p; return IIIMF_STATUS_SUCCESS; } IIIMF_status iiimf_im_user_set( IIIMF_im * im, const char * username, const char * password) { IIIMF_role_data_client * client; client = im->role_data.client; if (username) { if (client->user_name) free(client->user_name); client->user_name = strdup(username); } if (password) { if (client->password) free(client->password); client->password = strdup(password); } return IIIMF_STATUS_SUCCESS; } IIIMF_status iiimf_role_data_client_new( const char * server_node, const char * service, IIIMF_role_data_client ** client_ret) { IIIMF_role_data_client * client; struct passwd * pwd; IIIMF_status status; struct utsname name; status = IIIMF_STATUS_CONFIG; client = (IIIMF_role_data_client *)malloc(sizeof (IIIMF_role_data_client)); if (NULL == client) return IIIMF_STATUS_MALLOC; client->user_name = NULL; client->home_dir = NULL; client->node = NULL; client->server_node = ((NULL == server_node) ? NULL : strdup(server_node)); client->service = ((NULL == service) ? NULL : strdup(service)); client->type = strdup(CLIENT_TYPE); client->os_name = NULL; client->os_arch = NULL; client->os_version = NULL; client->X_display_name = NULL; client->X_server_vendor = NULL; pwd = getpwuid(geteuid()); if (NULL == pwd) { iiimf_role_data_client_delete(client); return IIIMF_STATUS_CONFIG; } client->user_name = strdup(pwd->pw_name); client->home_dir = strdup(pwd->pw_dir); endpwent(); if ((NULL == client->user_name) || (NULL == client->home_dir) || ((NULL != server_node) && (NULL == client->server_node)) || ((NULL != service) && (NULL == client->service))) { iiimf_role_data_client_delete(client); return IIIMF_STATUS_MALLOC; } if (-1 == uname(&name)) { iiimf_role_data_client_delete(client); return IIIMF_STATUS_CONFIG; } client->node = strdup(name.nodename); client->os_name = strdup(name.sysname); client->os_version = strdup(name.release); #if defined(OS_ARCH) client->os_arch = strdup(OS_ARCH); #else /* !OS_ARCH */ #if defined(HAVE_SYSINFO) && defined(SI_ARCHITECTURE) ret = sysinfo(SI_ARCHITECTURE, arch, sizeof (arch)); if (-1 == ret) { client->os_arch = NULL; } else if ((0 == strcmp(arch, "sparc")) || (0 == strcmp(arch, "ppc"))) { client->os_arch = strdup(arch); } else if (0 == strcmp(arch, "i386")) { client->os_arch = strdup("x86"); } else { client->os_arch = strdup("Unknown"); } #else /* !HAVE_SYSINFO || !SI_ARCHITECTURE */ client->os_arch = strdup("Unknown"); #endif /* !HAVE_SYSINFO || !SI_ARCHITECTURE */ #endif /* !OS_ARCH */ if ((NULL == server_node) && (NULL == service)) { status = iiimf_role_data_client_environ_server(client); if (IIIMF_STATUS_SUCCESS != status) { status = iiimf_role_data_client_file_server(client); } if (IIIMF_STATUS_SUCCESS != status) { status = iiimf_role_data_client_file_compat_server(client); } } if (NULL == client->server_node) { client->server_node = strdup(NODE_DEFAULT); } if (NULL == client->service) { client->service = strdup(SERVICE_DEFAULT); } if ((NULL == client->node) || (NULL == client->service)) { iiimf_role_data_client_delete(client); return IIIMF_STATUS_MALLOC; } *client_ret = client; return IIIMF_STATUS_SUCCESS; } void iiimf_role_data_client_delete(IIIMF_role_data_client * client) { if (client->user_name) free(client->user_name); if (client->password) free(client->password); if (client->home_dir) free(client->home_dir); if (client->node) free(client->node); if (client->server_node) free(client->server_node); if (client->service) free(client->service); if (client->type) free(client->type); if (client->os_name) free(client->os_name); if (client->os_arch) free(client->os_arch); if (client->os_version) free(client->os_version); if (client->X_display_name) free(client->X_display_name); if (client->X_server_vendor) free(client->X_server_vendor); free(client); } static void auth_password_generate(char * password, size_t length) { int fd; unsigned int seed; int c; int i; int r; unsigned int * p; size_t n; char * c62; *(password + length) = '\0'; c62 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; fd = open("/dev/random", O_RDONLY, 0); if (0 <= fd) { r = 0; n = ((sizeof (unsigned int)) * length); p = (unsigned int * )malloc(n); if (NULL != p) { r = read(fd, p, n); } (void)close(fd); if (r == length) { for (i = 0; i < length; i++) { *(password + i) = *(c62 + (*(p + i) % 62)); } free(p); return; } free(p); } seed = (time(NULL) + getpid()); srand(seed); for (i = 0; i < length; i++) { c = rand(); *(password + i) = *(c62 + (c % 62)); } return; } static int auth_password_file_init(char * password_file, size_t home_dir_len) { int fd; char pwd_buf[AUTH_PASSWORD_LEN + 1]; struct stat st; ssize_t len; /* ${HOME}/.iiim */ (void)strcpy(password_file + home_dir_len, CONFIG_DIR_BASE); if (0 != stat(password_file, &st)) { if (ENOENT != errno) return -1; if (0 != mkdir(password_file, 0777)) return -1; } /* ${HOME}/.iiim/auth */ (void)strcpy(password_file + home_dir_len, CONFIG_DIR_AUTH); if (0 != stat(password_file, &st)) { if (ENOENT != errno) return -1; if (0 != mkdir(password_file, 0700)) return -1; } /* ${HOME}/.iiim/auth/password */ (void)strcpy(password_file + home_dir_len, CONFIG_FILE_PASSWD); fd = open(password_file, O_CREAT | O_WRONLY, 0600); if (-1 == fd) return -1; auth_password_generate(pwd_buf, AUTH_PASSWORD_LEN); len = write(fd, pwd_buf, AUTH_PASSWORD_LEN + 1); (void)close(fd); if ((AUTH_PASSWORD_LEN + 1) == len) { return 0; } else { return -1; } } static int auth_password_file_open(const char * home_dir) { char * password_file; char * home_env; int home_dir_len; int len; struct stat st; int fd; password_file = NULL; if (NULL == home_dir) { home_env = getenv("HOME"); if (NULL != home_env) { home_dir = home_env; } if (NULL == home_dir) { return -1; } } home_dir_len = strlen(home_dir); len = (home_dir_len + 1 + PASSWORD_FILE_LEN); password_file = malloc(len + 1); if (NULL == password_file) { return -1; } (void)strcpy(password_file, home_dir); *(password_file + home_dir_len) = '/'; home_dir_len += 1; (void)strcpy(password_file + home_dir_len, CONFIG_FILE_PASSWD); if (0 != stat(password_file, &st)) { (void)auth_password_file_init(password_file, home_dir_len); } fd = open(password_file, O_RDONLY, 0); free(password_file); return fd; } IIIMF_status iiimf_role_data_client_auth_password(IIIMF_im * im, char ** password_ret) { #if 0 char * password; int fd; int len; int i; char pwd_buf[AUTH_PASSWORD_LEN + 1]; if (im->role_data.client->password) { *password_ret = strdup(im->role_data.client->password); if (!(*password_ret)) return IIIMF_STATUS_MALLOC; } else { password = NULL; fd = -1; fd = auth_password_file_open(im->role_data.client->home_dir); if (-1 == fd) return IIIMF_STATUS_CONFIG; len = read(fd, pwd_buf, AUTH_PASSWORD_LEN); (void)close(fd); if (AUTH_PASSWORD_LEN != len) return IIIMF_STATUS_CONFIG; for (i = 0; i < AUTH_PASSWORD_LEN; i++) { if (0 == isalnum(pwd_buf[i])) { return IIIMF_STATUS_CONFIG; } } if (AUTH_PASSWORD_LEN != i) { return IIIMF_STATUS_CONFIG; } password = malloc(AUTH_PASSWORD_LEN + 1); if (NULL == password) return IIIMF_STATUS_MALLOC; (void)memcpy(password, pwd_buf, AUTH_PASSWORD_LEN); *(password + AUTH_PASSWORD_LEN) = '\0'; *password_ret = password; } #endif *password_ret = strdup(""); return IIIMF_STATUS_SUCCESS; } /* Local Variables: */ /* c-file-style: "iiim-project" */ /* End: */