/* PureAdmin * Copyright (C) 2003 Isak Savo * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* * Misc functions used to handle system accounts * * Copyright (C) 2006 Isak Savo */ #include #include #include #include #include #include #include #include #include #include "globals.h" #include "cfg.h" #include "helper.h" #include "system_accounts.h" /*******************************************/ /* Private Functions */ /*******************************************/ typedef enum { USERS, GROUPS } AccountType; static gint cmp_system_accounts (SystemAccount *a, SystemAccount *b) { if (a->id < b->id) return -1; else if (a->id > b->id) return 1; return 0; } static GList *internal_get_system_ids(AccountType what) { FILE *fl; GList *retval = NULL; gchar buf[LINE_MAX], **arr; gchar *filename; if (what == USERS) filename = "/etc/passwd"; else filename = "/etc/group"; if ((fl = fopen (filename, "r")) == NULL) { pur_log_wrn ("Unable to open file %s: %s", filename, g_strerror(errno)); return NULL; } while (fgets (buf, LINE_MAX, fl)) { arr = g_strsplit (buf, ":", -1); if (arr_count (arr) < 2) { g_strfreev (arr); continue; } SystemAccount *account = g_new0 (SystemAccount, 1); if (arr[0][0] && arr[2][0] && atoi (arr[2]) > 0) { account->id = atoi(arr[2]); account->name = g_strdup (arr[0]); retval = g_list_insert_sorted (retval, account, (GCompareFunc) cmp_system_accounts); } g_strfreev (arr); } fclose (fl); return g_list_first (retval); } static GList *internal_get_system_ids_str(AccountType what) { GList *list = internal_get_system_ids(what); GList *node = list; while (node) { SystemAccount *a = (SystemAccount*) node->data; node->data = g_strdup_printf("%s (%d)", a->name, a->id); system_account_free(a); node = g_list_next(node); } return list; } static gboolean internal_get_account_id_exists(AccountType what, guint id) { GList *ids = internal_get_system_ids(what); GList *node = ids; gboolean found = FALSE; while (node) { SystemAccount *a = (SystemAccount*) node->data; if (a->id == id) { found = TRUE; /* We keep iterating the entire loop since we want to free all node data */ } system_account_free(a); node = g_list_next(node); } g_list_free(ids); return found; } static guint internal_find_available_account_id(AccountType what, guint min_id) { GList *list = internal_get_system_ids(what); GList *node = list; guint last_id = 0, rv = 0; while (node) { SystemAccount *a = (SystemAccount *) node->data; node = g_list_next(node); if (a->id <= min_id) continue; if (a->id > last_id + 1 && last_id > min_id) { rv = last_id + 1; break; } last_id = a->id; } system_account_list_free(list); if (rv == 0) rv = last_id + 1; return rv; } /*******************************************/ /* Public Functions */ /*******************************************/ GList *sys_get_user_ids (void) { return internal_get_system_ids(USERS); } GList *sys_get_group_ids (void) { return internal_get_system_ids(GROUPS); } GList *sys_get_user_ids_str (void) { return internal_get_system_ids_str(USERS); } GList *sys_get_group_ids_str (void) { return internal_get_system_ids_str(GROUPS); } gboolean sys_get_uid_exists(guint uid) { return internal_get_account_id_exists(USERS, uid); } gboolean sys_get_gid_exists(guint gid) { return internal_get_account_id_exists(GROUPS, gid); } guint sys_get_available_user_id (guint min_id) { return internal_find_available_account_id(USERS, min_id); } guint sys_get_available_group_id (guint min_id) { return internal_find_available_account_id(GROUPS, min_id); } gboolean sys_create_user (const gchar *username, guint userid, guint groupid, GError **err) { gint child_retval; gboolean ret; GError *spawn_err = NULL; g_return_val_if_fail (err == NULL || *err == NULL, FALSE); if (!cfg.cmd_useradd) { g_set_error (err, PUREADMIN_MAIN_ERROR, PA_MAIN_ERROR_COMMAND_NOT_FOUND, "Command 'useradd' isn't available"); return FALSE; } if (!MISC_VALID_STRING(username)) { g_set_error(err, PUREADMIN_MAIN_ERROR, PA_MAIN_ERROR_FAILED, "Invalid username"); return FALSE; } gchar *cmd = g_strdup_printf ("%s -u %d -g %d -d /dev/null -c 'PureFTP User' -s /etc %s", cfg.cmd_useradd, userid, groupid, username); pur_log_dbg ("Running command [%s]", cmd); ret = g_spawn_command_line_sync (cmd, NULL, NULL, &child_retval, &spawn_err); g_free (cmd); child_retval = (signed char) WEXITSTATUS(child_retval); if (child_retval){ g_set_error (err, PUREADMIN_MAIN_ERROR, PA_MAIN_ERROR_FAILED, "Child '%s' returned abnormally: %d", cfg.cmd_useradd, child_retval); return FALSE; } if (!ret) { g_set_error (err, PUREADMIN_MAIN_ERROR, PA_MAIN_ERROR_FAILED, "Couldn't spawn child process '%s': %s", cfg.cmd_useradd, spawn_err->message); g_error_free(spawn_err); return FALSE; } return TRUE; } /* Creates a new group with the specified name and id. If 'groupid' is 0, the groupadd command will pick one * instead. */ gboolean sys_create_group (const gchar *groupname, guint groupid, GError **err) { gboolean ret; gint child_retval; GError *spawn_err = NULL; g_return_val_if_fail (err == NULL || *err == NULL, FALSE); if (!cfg.cmd_groupadd) { g_set_error (err, PUREADMIN_MAIN_ERROR, PA_MAIN_ERROR_COMMAND_NOT_FOUND, "Command 'groupadd' isn't available"); return FALSE; } if (!MISC_VALID_STRING(groupname)) { g_set_error(err, PUREADMIN_MAIN_ERROR, PA_MAIN_ERROR_FAILED, "One or more arguments was incorrect"); return FALSE; } gchar *cmd; if (groupid > 0) cmd = g_strdup_printf ("%s -g %d %s", cfg.cmd_groupadd, groupid, groupname); else cmd = g_strdup_printf ("%s %s", cfg.cmd_groupadd, groupname); pur_log_dbg ("Running command [%s]", cmd); ret = g_spawn_command_line_sync (cmd, NULL, NULL, &child_retval, &spawn_err); g_free (cmd); child_retval = (signed char) WEXITSTATUS(child_retval); if (child_retval){ g_set_error (err, PUREADMIN_MAIN_ERROR, PA_MAIN_ERROR_FAILED, "Child '%s' returned abnormally: %d", cfg.cmd_groupadd, child_retval); return FALSE; } if (!ret) { g_set_error (err, PUREADMIN_MAIN_ERROR, PA_MAIN_ERROR_FAILED, "Couldn't spawn child process '%s': %s", cfg.cmd_groupadd, spawn_err->message); g_error_free(spawn_err); return FALSE; } return TRUE; } /* Frees one SystemAccount object */ void system_account_free(SystemAccount *account) { if (account == NULL) return; g_free (account->name); g_free (account); } /* Frees a GList containing SystemAccount objects */ void system_account_list_free (GList *list) { g_list_foreach(list, (GFunc) system_account_free, NULL); g_list_free (list); }