diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..73a87e5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,60 @@ +*# +*.o +*~ +/.deps +/Makefile +/config.h +fjernet +/mysql-dbadm +/mysql-useradm +/pwlex.c +/pwyacc.c +/pwyacc.h +/stamp-h + +### Autotools ### +# http://www.gnu.org/software/automake + +Makefile.in +/ar-lib +/mdate-sh +/py-compile +/test-driver +/ylwrap +/mkinstalldirs +/INSTALL + +# http://www.gnu.org/software/autoconf + +autom4te.cache +/autoscan.log +/autoscan-*.log +/aclocal.m4 +/compile +/config.guess +/config.h.in +/config.log +/config.status +/config.sub +/configure +/configure.scan +/depcomp +/install-sh +/missing +/stamp-h1 + +# https://www.gnu.org/software/libtool/ + +/ltmain.sh + +# http://www.gnu.org/software/texinfo + +/texinfo.tex + +# http://www.gnu.org/software/m4/ + +m4/libtool.m4 +m4/ltoptions.m4 +m4/ltsugar.m4 +m4/ltversion.m4 +m4/lt~obsolete.m4 diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..e69de29 diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..eeb586b --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + 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 + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..f030463 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,25 @@ +v0.50 (2006-11-20) geirha@itea.ntnu.no + * added create_tmp_table_priv + * added lock_tables_priv + * some memory leaks fixed + +v0.46 (2005-06-27) knuta@itea.ntnu.no + * Replaced deprecated functions not supported in SLES9 + +v0.42-0.45 (2002-03-07 - 2004-12-08) unknown + * Various undocumented fixes, at least something about length + of group names, I think + +v0.41 (2002-03-07) tlan@stud.ntnu.no + * Fixed a seg.fault bug in is_password_set()-routine. + +v0.4 (2002-03-07) tlan@stud.ntnu.no + * added ability to make users/dbs beginning with group-names. + * added index-priv + * added alter-priv + + +Mon Jul 6 11:58:10 1998 Vebjorn Ljosa + * Initial version. + + diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..e3a0bd7 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,9 @@ +bin_PROGRAMS = mysql-dbadm mysql-useradm +mysql_admutils_SOURCE = common.c mysql-admutils.h pwyacc.y pwlex.l pwfile.c +mysql_dbadm_SOURCES = mysql-dbadm.c $(mysql_admutils_SOURCE) +mysql_useradm_SOURCES = mysql-useradm.c $(mysql_admutils_SOURCE) +BUILT_SOURCES = pwyacc.c pwyacc.h pwlex.c +YFLAGS = -d +INCLUDES = @MYSQL_INCLUDE@ -DSYSCONFDIR=\"$(sysconfdir)\" +LDADD = @MYSQL_LIBS@ @LEXLIB@ +LDFLAGS = @MYSQL_LFLAGS@ diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..e69de29 diff --git a/README b/README new file mode 100644 index 0000000..e69de29 diff --git a/acconfig.h b/acconfig.h new file mode 100644 index 0000000..76e5baf --- /dev/null +++ b/acconfig.h @@ -0,0 +1,29 @@ +/* + * acconfig.h + * + * @(#) $Header: /tmp/cvs/mysql-admutils/acconfig.h,v 1.2 2002-02-27 08:49:48 knutpett Exp $ + * + */ + + +/* Define the name of the package */ +#undef PACKAGE + +/* Define the version of the package */ +#undef VERSION + +/* Define if MySQL is installed */ +#undef HAVE_MYSQL + +/* Define if you have the header file. */ +#undef HAVE_MATH_H + +/* Define if libmath is available */ +#undef HAVE_LIBM + +/* Define if libnsl is available */ +#undef HAVE_LIBNSL + +/* Define if libsocket is available */ +#undef HAVE_LIBSOCKET + diff --git a/common.c b/common.c new file mode 100644 index 0000000..84d9603 --- /dev/null +++ b/common.c @@ -0,0 +1,276 @@ +/* + * @(#) $Header: /tmp/cvs/mysql-admutils/common.c,v 1.9 2007-02-27 14:10:07 geirha Exp $ + * + * functions used by mysql-dbadm.c and mysql-useradm.c + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mysql-admutils.h" + +char *program_name; + +static char *rcsheader = "@(#) " PACKAGE " " VERSION " orakel@ntnu.no $Header: /tmp/cvs/mysql-admutils/common.c,v 1.9 2007-02-27 14:10:07 geirha Exp $"; + + +int +version() +{ + printf("%s %s\n", program_name, rcsheader); + exit(0); +} + + +int +wrong_use(char *format, ...) +{ + va_list ap; + + if (format) { + fprintf(stderr, "%s: ", program_name); + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(ap); + putchar('\n'); + } + + fprintf(stderr, "Try `%s --help' for more information.\n", program_name); + + return 1; +} + + +/* always returns 1. */ +int +dberror(MYSQL *pmysql, char *format, ...) +{ + const char *errmsg; + va_list ap; + + fprintf(stderr, "%s: ", program_name); + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(ap); + fprintf(stderr, "\n"); + + if (pmysql) { + errmsg = mysql_error(pmysql); + if ((errmsg) && (strcmp(errmsg, "") != 0)) + fprintf(stderr, "mysql: %s\n", errmsg); + + mysql_close(pmysql); + } + + return 1; +} + + +/* always returns 1. */ +int +fatal_error(char *format, ...) +{ + va_list ap; + + fprintf(stderr, "%s: ", program_name); + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(ap); + fprintf(stderr, "\n"); + + return 1; +} + + +/* decides if the UNIX user is entitled to the MySQL database or MySQL user. */ +int +owner(char *name) +{ + struct passwd *p; + + p = getpwuid(getuid()); + if (!p) { + dberror(NULL, "Failed to look up your UNIX username."); + exit(1); + } + + if (strcmp(name, p->pw_name) == 0) + return 1; /* OK */ + + if ((strncmp(name, p->pw_name, strlen(p->pw_name)) == 0) && + (*(name + strlen(p->pw_name)) == '_')) + return 1; /* OK */ + + return 0; /* not owner if we get as far as this */ +} + +/** + * Decides if the user is member of a group. The Unix group can't contain any + * '_'. i.e 'fidi_s' won't be accepted. + */ +int +member(char *gr) { + char *username; + + char group[65]; + struct group *g; + + struct passwd *p; + char *foo; + + /* size_t i = 0; */ + + /* Get username */ + p = getpwuid(getuid()); + if (!p) { + fprintf(stderr, "Failed to look up your UNIX username."); + return 0; + } + username = p->pw_name; + + /* Copy string, but cut at '_' */ + strncpy(group, gr, 64); + group[64] = '\0'; + + // ettersom man kan få inn gruppenavn med underscore i, må man rett og + // slett prøve seg fram for å sjekke om det er en gruppe personen er med + // i. + // eksempel: www_esb_test_users, hvor personen ikke er med i www_est_test + // (som kanskje finnes), men er med i www_esb. + // group er databasenavnet som brukeren ville ha. Vi må finne gruppenavnet + // ut av den, og stripper av en og en underscore til vi finner noe som + // passer. + while ( 1 ) { + +#if DEBUG +// printf("gr = %s, group = %s, foo = %s\n", gr, group, foo); +#endif + g = getgrnam(group); +#if DEBUG + printf("tror gruppenavnet er: %s\n", group); +#endif + + // hvis gruppen ikke finnes, må vi i allefall prøve å se om + // det er noe lengre inn. + if (g) { + /* Check if user is member of group */ + while(*g->gr_mem != NULL) { + char * member = *g->gr_mem++; +#if DEBUG + printf("Medlem: %s\n", member); +#endif + + if (strcmp(member,username) == 0) { +#if DEBUG + printf("You have access to '%s'\n", gr); +#endif + return 1; /* OK */ + } + } +#if DEBUG + printf("You have no access to '%s'\n", gr); +#endif + } // if + // brukeren var ikke med i gruppen, så vi må prøve neste. + + // finner den siste _-en i navnet. + foo = strrchr(group, '_'); + if (foo == NULL) { + // hvis vi har kommet helt til bunn, og fremdeles + // ikke har blitt avbrutt, fantes det ingen slik gruppe. + return 0; + } + // det som nå er bak siste underscore er passe uinteressant, så + // vi flytter slutten av strengen litt framover. + *foo = '\0'; + + } // while +} + +/* return a list of the user's groupnames */ +/* numgroups is the total number of groups found */ +char **get_group_names(int *numgroups) +{ + char **grouplist; + gid_t *gids = NULL; + int real_nr_groups, nr_groups, i; + struct group *g; + + nr_groups = 0; + + /* Find number and allocate */ + nr_groups = getgroups(0, NULL); + if (nr_groups == -1) { + dberror(NULL, "Error while trying to find group count"); + return NULL; + } + gids = malloc(nr_groups*sizeof(gid_t)); + + /* Fetch group IDs */ + nr_groups = getgroups(nr_groups, gids); + if (nr_groups == -1) { + dberror(NULL, "Error while trying to fetch group info"); + return NULL; + } + + grouplist = malloc((nr_groups + 1)*sizeof(char *)); + real_nr_groups = 0; + + for (i = 0; i < nr_groups; i++) { + g = getgrgid(gids[i]); + + /* Go to next grp if it doesn't have a name */ + if (g != NULL) { + grouplist[real_nr_groups++] = strdup(g->gr_name); + } else { + fprintf(stderr, "Omitting gid %d, no entry in group-file.\n", gids[i]); + } + } + grouplist[real_nr_groups] = NULL; + + free(gids); + + *numgroups = real_nr_groups; + + return grouplist; +} + + +int +reload(MYSQL *pmysql) +{ + return mysql_reload(pmysql); +} + +/* same as strcpy, but returns a pointer to the end of dest instead of start */ +char *strmov(char *dest, const char *src) { + while ((*dest++ = *src++)) + ; + return dest-1; +} + +/* New database and user names may only use these characters in their + identifier */ +const char name_validchars[] = + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-"; + +/* Returns true if dbname contains only characters in name_validchars. */ +int name_isclean(char* name) { + int reallen, cleanlen; + reallen = strlen(name); + cleanlen = strspn(name, name_validchars); + return (reallen == cleanlen); +} + diff --git a/configline b/configline new file mode 100644 index 0000000..49f7a3f --- /dev/null +++ b/configline @@ -0,0 +1,44 @@ + +vanlig, på linux. +./configure --with-mysql=/usr/ --sysconfdir=/etc/ + +vi burde kanskje komplisere med libmysqlclient statisk, +sånn at vi slipper avhengigheter mot en spesiell rpmpakke. + + +elefant:~/mysql-admutils$ diff -u ../Makefile.org Makefile +--- ../Makefile.org Tue Apr 8 20:20:00 2003 ++++ Makefile Tue Apr 8 20:22:27 2003 +@@ -62,8 +62,8 @@ + LEX = flex + MAKEINFO = makeinfo + MYSQL_INCLUDE = -I/store/include/mysql +-MYSQL_LFLAGS = -L/store/lib/mysql +-MYSQL_LIBS = -lmysqlclient ++MYSQL_LFLAGS = ++MYSQL_LIBS = + PACKAGE = mysql-admutils + SYSCONFDIR = + VERSION = 0.43 +@@ -76,8 +76,8 @@ + BUILT_SOURCES = pwyacc.c pwyacc.h pwlex.c + YFLAGS = -d + INCLUDES = -I/store/include/mysql -DSYSCONFDIR=\"$(sysconfdir)\" +-LDADD = -lmysqlclient -lfl +-LDFLAGS = -L/store/lib/mysql ++LDADD = /store/lib/mysql/libmysqlclient.a -lfl ++LDFLAGS = + ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 + mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs + CONFIG_HEADER = config.h + + + + + + + + +$Id: configline,v 1.2 2003-04-08 18:31:53 lkarsten Exp $ +$Source: /tmp/cvs/mysql-admutils/configline,v $ + diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..9b7bc2b --- /dev/null +++ b/configure.ac @@ -0,0 +1,98 @@ +dnl Process this file with autoconf to produce a configure script. +AC_INIT(mysql-dbadm.c) +AM_CONFIG_HEADER(config.h) +AM_INIT_AUTOMAKE(mysql-admutils, 0.46) +AM_SANITY_CHECK + +dnl Checks for programs. +AC_PROG_CC +AC_PROG_CPP +AM_PROG_LEX +AC_PROG_YACC + +dnl Checks for libraries. +AC_CHECK_LIB(c, floor, [:], [ + AC_CHECK_LIB(m, floor, [ + LIBS="$LIBS -lm" + AC_DEFINE(HAVE_LIBM) ], []) ]) +AC_CHECK_LIB(mysqlclient, mysql_connect, [ + LIBS="$LIBS -lmysqlclient" ], []) +AC_CHECK_LIB(c, gethostbyaddr, [:], [ + AC_CHECK_LIB(nsl, gethostbyaddr, [ + LIBS="$LIBS -lnsl" + AC_DEFINE(HAVE_LIBNSL) ], []) ]) +AC_CHECK_LIB(c, socket, [:], [ + AC_CHECK_LIB(socket, socket, [ + LIBS="$LIBS -lsocket" + AC_DEFINE(HAVE_LIBSOCKET) ], []) ]) + +AC_MSG_CHECKING(for MySQL support) +AC_ARG_WITH(mysql, +[ --with-mysql[=DIR] Include MySQL support. DIR is the MySQL base + install directory, defaults to /usr/local.], +[ + if test "$withval" != "no"; then + if test "$withval" = "yes"; then + MYSQL_INCDIR=/usr/local/include/mysql + MYSQL_LIBDIR=/usr/local/lib/mysql + else + if test -f $withval/include/mysql/mysql.h; then + MYSQL_INCDIR=$withval/include/mysql + MYSQL_LIBDIR=$withval/lib/mysql + elif test -f $withval/include/mysql.h; then + MYSQL_INCDIR=$withval/include + MYSQL_LIBDIR=$withval/lib + else + AC_MSG_RESULT(no) + AC_MSG_ERROR(Invalid MySQL directory - unable to find mysql.h under $withval) + fi + fi + MYSQL_INCLUDE=-I$MYSQL_INCDIR + MYSQL_LFLAGS="-L$MYSQL_LIBDIR -Wl,-R,$MYSQL_LIBDIR" + #MYSQL_LFLAGS="-L$MYSQL_LIBDIR" + MYSQL_LIBS=-lmysqlclient + + AC_DEFINE(HAVE_MYSQL) + AC_MSG_RESULT(yes) + + dnl check for errmsg.h, which isn't installed by some versions of 3.21 + old_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $MYSQL_INCLUDE" + AC_CHECK_HEADERS(errmsg.h) + CPPFLAGS="$old_CPPFLAGS" + else + AC_MSG_RESULT(no) + fi +],[ + AC_MSG_RESULT(no) + AC_MSG_RESULT("Giving up - You need to install MySQL somewhere"); exit +]) +AC_SUBST(MYSQL_LIBS) +AC_SUBST(MYSQL_LFLAGS) +AC_SUBST(MYSQL_INCLUDE) + + +AC_MSG_CHECKING([whether to enable -Wall -pedantic]) +AC_ARG_ENABLE(warnings, +[ --enable-warnings Enable -Wall -pedantic if using gcc.], +[ if test -n "$GCC"; then + AC_MSG_RESULT(adding -Wall -pedantic to CFLAGS.) + CFLAGS="$CFLAGS -Wall -pedantic" + fi +],AC_MSG_RESULT(no)) + + +dnl Checks for header files. +AC_HEADER_STDC +AC_CHECK_HEADERS(unistd.h) + +dnl Checks for typedefs, structures, and compiler characteristics. +AC_C_CONST +AC_TYPE_SIZE_T + +dnl Checks for library functions. +AC_FUNC_VPRINTF + +AC_SUBST(SYSCONFDIR) + +AC_OUTPUT(Makefile, [date > stamp-h]) diff --git a/mysql-admutils.h b/mysql-admutils.h new file mode 100644 index 0000000..1ee0cdc --- /dev/null +++ b/mysql-admutils.h @@ -0,0 +1,51 @@ +/* + * @(#) $Header: /tmp/cvs/mysql-admutils/mysql-admutils.h,v 1.3 2002-03-06 17:05:05 tlan Exp $ + * + */ + +#ifndef MYSQL_ADMUTILS_H +#define MYSQL_ADMUTILS_H + +extern char *program_name; + +/* defined in pwfile.c */ +extern const char *db_server; +extern const char *db_user; +extern const char *db_passwd; +extern const char *db_name; + +extern int +wrong_use(char *format, ...); + +extern int +fatal_error(char *format, ...); + +extern int +owner(char *name); + +extern int member(char *gr); + +extern char **get_group_names(int *numgroups); + +extern int +version(void); + +extern int +read_config_file(void); + +/* same as strcpy, but returns a pointer to the end of dest instead of start */ +extern char *strmov(char *, const char *); + +extern int name_isclean(char*); + +#ifdef _mysql_h + +extern int +dberror(MYSQL *pmysql, char *format, ...); + +extern int +reload(MYSQL *pmysql); + +#endif + +#endif /* MYSQL_ADMUTILS_H */ diff --git a/mysql-dbadm.c b/mysql-dbadm.c new file mode 100644 index 0000000..d80ed86 --- /dev/null +++ b/mysql-dbadm.c @@ -0,0 +1,707 @@ +/* + * @(#) $Header: /home/stud/admin/cvs/mysql-admutils/mysql-dbadm.c,v 1.20 2007/06/04 08:40:54 geirha Exp $ + * + * mysql-dbadm.c + * + */ + +#include "config.h" +#include "mysql-admutils.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +char * +strchr_whitespace(const char *s) +{ + char *sp, *tab; + sp = strchr(s, ' '); + tab = strchr(s, '\t'); + if (sp == NULL) + return tab; + if (tab == NULL) + return sp; + return sp < tab ? sp : tab; /* if both are found, return the first one */ +} + +char * +strtok_whitespace(char *s) +{ + static char *cp; + char *r; + + r = s ? s : cp; + if (r == NULL) + return NULL; + cp = strchr_whitespace(r); + if (cp == NULL) + return r; + while ((*cp == ' ') || (*cp == '\t')) + *cp++ = '\0'; + return r; +} + + +int +valid_priv(const char *s) +{ + if (s == NULL) return 0; +#define ACCEPT(x) if (strcmp(s, x) == 0) return 1 + ACCEPT("Y"); + ACCEPT("N"); + ACCEPT("y"); + ACCEPT("n"); +#undef ACCEPT + return 0; /* not a valid priv */ +} + + +int +usage() +{ + printf("Usage: %s COMMAND [DATABASE]...\n", program_name); + printf("Create, drop og edit permission for the DATABASE(s),\n"); + printf("as determined by the COMMAND. Valid COMMANDs:\n"); + printf("\n"); + printf(" create create the DATABASE(s).\n"); + printf(" drop delete the DATABASE(s).\n"); + printf(" show give information about the DATABASE(s), or, if\n"); + printf(" none are given, all the ones you own.\n"); + printf(" editperm change permissions for the DATABASE(s). Your\n"); + printf(" favorite editor will be started, allowing you\n"); + printf(" to make changes to the permission table.\n"); + printf(" Run '%s --help-editperm' for more\n" + " information.\n",program_name); + printf("\n"); + printf("Report bugs to orakel@ntnu.no\n"); + return 0; +} + +int +usage_editperm() +{ + printf("Usage: %s editperm [DATABASE]\n", program_name); + printf("Edit permissions for the DATABASE. Running this command will\n"); + printf("spawn the editor stored in the $EDITOR environment variable.\n"); + printf("(pico will be used if the variable is unset)\n"); + printf("\n"); + printf("The file should contain one line per user, starting with the\n"); + printf("username and followed by ten Y/N-values seperated by whitespace.\n"); + printf("Lines starting with # are ignored.\n"); + printf("\n"); + printf("The Y/N-values corresponds to the following mysql privileges:\n"); + printf(" Select - Enables use of SELECT\n"); + printf(" Insert - Enables use of INSERT\n"); + printf(" Update - Enables use of UPDATE\n"); + printf(" Delete - Enables use of DELETE\n"); + printf(" Create - Enables use of CREATE TABLE\n"); + printf(" Drop - Enables use of DROP TABLE\n"); + printf(" Alter - Enables use of ALTER TABLE\n"); + printf(" Index - Enables use of CREATE INDEX and DROP INDEX\n"); + printf(" Temp - Enables use of CREATE TEMPORARY TABLE\n"); + printf(" Lock - Enables use of LOCK TABLE\n"); + printf(" References - Enables use of REFERENCES\n"); + printf("\n"); + printf("Report bugs to orakel@ntnu.no\n"); + return 0; +} + + +int +create(MYSQL *pmysql, char *db) +{ + // hvis man forsøker å dra mysql_create_db() på en database som + // allerede finnes, så henger bare hele sql-kallet. Vi må derfor + // forsøke å selecte databasen først. Ettersom man alltid er root, + // går jo dette vanligvis bra. + // finner ut om denne finnes fra før. + if (!mysql_select_db(pmysql, db)) { + return dberror(pmysql, "Database '%s' already exists.", db); + } + mysql_select_db(pmysql, "mysql"); + // oppretter databasen. + char query[1024], *end; + end = strmov(query, "CREATE DATABASE `"); + end += mysql_real_escape_string(pmysql, end, db, strlen(db)); + *end++ = '`'; + *end = '\0'; +#ifdef DEBUG + printf("query: %s\n", query); +#endif + if (mysql_query(pmysql, query)) + return dberror(pmysql, "Cannot create database '%s'.", db); + fprintf(stderr, "Database '%s' created.\n", db); + return 0; +} + + +int +drop(MYSQL *pmysql, char *db) +{ + char query[1024], *end; + + end = strmov(query, "DELETE FROM db WHERE db = '"); + end += mysql_real_escape_string(pmysql, end, db, strlen(db)); + *end++ = '\''; + *end = '\0'; + +#ifdef DEBUG + printf("query: %s\n", query); +#endif + if (mysql_query(pmysql, query)) + dberror(pmysql, "Failed to delete permissions for database '%s'.", db); + + if (mysql_select_db(pmysql, db)) { + dberror(pmysql, "Database '%s' doesn't exists.", db); + return 0; + } + mysql_select_db(pmysql, "mysql"); + + end = strmov(query, "DROP DATABASE `"); + end += mysql_real_escape_string(pmysql, end, db, strlen(db)); + *end++ = '`'; + *end = '\0'; + +#ifdef DEBUG + printf("query: %s\n", query); +#endif + if (mysql_query(pmysql, query)) + return dberror(pmysql, "Cannot drop database '%s'.", db); + + fprintf(stderr, "Database '%s' dropped.\n", db); + return 0; +} + +/* return a list of the user's databases */ +char ** +list(MYSQL *pmysql) +{ + char *wild; + char **usr_groups, **cp; /* holds all group names this dude is a member of [tlan]*/ + MYSQL_RES *res; + int rows, numgroups, numgroupdbs; + MYSQL_ROW row; + char **dblist; + int i, counter; + struct passwd *p; + + // variabler som brukes til å escape gruppenavnet til mysqlvennlig format. + char escaped_user[64]; + char *cp_kopi; + + p = getpwuid(getuid()); + if (!p) { + dberror(NULL, "Failed to lookup your UNIX username."); + exit(1); + } + + dblist = malloc(2 * sizeof(char*)); /* one for username (if used), rest is done below */ + + numgroupdbs = 0; + counter = 0; + + usr_groups = get_group_names(&numgroups); + cp = usr_groups; + while (cp && *cp) { // itererer over alle grupper en person er med i + if (*cp == NULL) + break; + +#ifdef DEBUG + printf("cp er %s\n", *cp); +#endif + + escaped_user[0] = '\0'; + cp_kopi=*cp; + +// itererer over bokstavene i gruppenavn, og escaper spesialtegn. + for(i=0; i<=strlen(cp_kopi); i++) { + // hvis % _ , så skriv \ og så tegn + if ((cp_kopi[i] == '_') || (cp_kopi[i] == '%')) { + strcat(escaped_user, "\\"); + } + + escaped_user[strlen(escaped_user) + 1] = '\0'; + escaped_user[strlen(escaped_user)] = cp_kopi[i]; + } // for + + wild = malloc(strlen(escaped_user)+4); + sprintf(wild, "%s\\_%%", escaped_user); + +#ifdef DEBUG + printf("dbadm: wildcard: '%s'\n", wild); +#endif + + res = mysql_list_dbs(pmysql, wild); + rows = mysql_num_rows(res); + + if (rows > 0) { + numgroupdbs += rows; + + dblist = realloc(dblist, (numgroupdbs+2) * sizeof(char *)); + + for (i = 0; i < rows; i++) { + if ((row = mysql_fetch_row(res))) { + dblist[counter++] = strdup(row[0]); + } + } + } + + + free(wild); + mysql_free_result(res); + cp++; + } + + wild = malloc(strlen(p->pw_name) + 4); + sprintf(wild, "%s\\_%%", p->pw_name); +#ifdef DEBUG + printf("dbadm: wildcard: '%s'\n", wild); +#endif + + res = mysql_list_dbs(pmysql, wild); + rows = mysql_num_rows(res); + dblist = realloc(dblist, (numgroupdbs+rows+2) * sizeof(char *)); + + if (!dblist) { + dberror(NULL, "Out of memory.\n"); + free(wild); + return NULL; + } + + for (i = 0; i < rows; i++) { + if ((row = mysql_fetch_row(res))) { + dblist[counter++] = strdup(row[0]); + } + } + mysql_free_result(res); + + res = mysql_list_dbs(pmysql, p->pw_name); + rows = mysql_num_rows(res); + if (rows == 1) + dblist[counter++] = strdup(p->pw_name); + + dblist[counter] = NULL; + + mysql_free_result(res); + free(wild); + for (i=0;inet.fd) != 0) + { + perror("Failed to close fd"); + exit(1); + } + + editor = getenv("EDITOR"); + if (!editor) + editor = "pico"; /* OK since editor won't be freed */ + + cmd = malloc(sizeof(char) * strlen(editor) + 7); + sprintf(cmd, "%s \"$1\"", editor); + + /* sh -c '$EDITOR "$1"' sh "$fn" */ + execlp("sh", "sh", "-c", cmd, "sh", fn, NULL); + perror("Failed to execute editor"); + fprintf(stderr, "Make sure the EDITOR environment variable contains" + " a valid editor\n"); + exit(1); + } + + if (exit_status != 0) + return 1; + + /* parse */ + f = fopen(fn, "r"); + lines = 0; + while (fgets(line, sizeof(line), f)) { + cp = strchr(line, '\n'); + if (cp) + *cp = '\0'; + + cp = line; + while (*cp && ((*cp == ' ') || (*cp == '\t'))) cp++; + if (*cp == '\0') continue; + if (*cp == '#') continue; + if (*cp == '\n') continue; + + #define STRTOK_WHITESPACE(res, start) \ + { if (!(res = strtok_whitespace(start))) continue; } + + STRTOK_WHITESPACE(user, cp); + if (strlen(user) < 1) + return dberror(NULL, "Invalid user '%s' in grant line %d.", user, + lines + 1); + if (strcmp(user, "%") == 0) + *user = '\0'; /* ugly, but it works... */ + + #define CHECK_PRIV(PRIV) \ + if (!valid_priv(PRIV)) return dberror(NULL, "Invalid value '%s' " \ + "in grant line %d.\nMake sure you fill in a value (Y or N) for " \ + "all ten privileges.\nRun '%s --help-editerm' for more " \ + "information.\nNo permissions have been set.", PRIV, lines + 1, \ + program_name) + + STRTOK_WHITESPACE(select_priv, NULL); + CHECK_PRIV(select_priv); + STRTOK_WHITESPACE(insert_priv, NULL); + CHECK_PRIV(insert_priv); + STRTOK_WHITESPACE(update_priv, NULL); + CHECK_PRIV(update_priv); + STRTOK_WHITESPACE(delete_priv, NULL); + CHECK_PRIV(delete_priv); + STRTOK_WHITESPACE(create_priv, NULL); + CHECK_PRIV(create_priv); + STRTOK_WHITESPACE(drop_priv, NULL); + CHECK_PRIV(drop_priv); + STRTOK_WHITESPACE(alter_priv, NULL); + CHECK_PRIV(alter_priv); + STRTOK_WHITESPACE(index_priv, NULL); + CHECK_PRIV(index_priv); + STRTOK_WHITESPACE(create_tmp_table_priv, NULL); + CHECK_PRIV(create_tmp_table_priv); + STRTOK_WHITESPACE(lock_tables_priv, NULL); + CHECK_PRIV(lock_tables_priv); + STRTOK_WHITESPACE(references_priv, NULL); + CHECK_PRIV(references_priv); + + #undef STRTOK_WHITESPACE + #undef CHECK_PRIV + + end = strmov(query, "INSERT INTO db (" + "host,db,user,select_priv,insert_priv," + "update_priv,delete_priv,create_priv," + "drop_priv,alter_priv,index_priv," + "create_tmp_table_priv,lock_tables_priv," + "references_priv" + ") VALUES ("); + + end = strmov(end, "'%'"); + + #define APPEND(VAR) {\ + *end++ = ',';\ + *end++ = '\'';\ + end += mysql_real_escape_string(pmysql, end, VAR, strlen(VAR));\ + *end++ = '\'';\ + } + + APPEND(db); + APPEND(user); + APPEND(select_priv); + APPEND(insert_priv); + APPEND(update_priv); + APPEND(delete_priv); + APPEND(create_priv); + APPEND(drop_priv); + APPEND(alter_priv); + APPEND(index_priv); + APPEND(create_tmp_table_priv); + APPEND(lock_tables_priv); + APPEND(references_priv); + *end++ = ')'; + *end = '\0'; + + #undef APPEND + + queries[lines] = strdup(query); + lines++; + if (lines >= MAX_GRANTS) + { + dberror(NULL, "Warning: Maximum of %d grants reached.\n", + MAX_GRANTS); + continue; + } + } /* while fgets ... */ + + unlink(fn); + fclose(f); + + /* now that we have checked the input for errors, we can safely + delete the old grants from the database and insert the new ones. */ + + end = strmov(query, "DELETE FROM db WHERE db = '"); + end += mysql_real_escape_string(pmysql, end, db, strlen(db)); + *end++ = '\''; + *end = '\0'; +#ifdef DEBUG + printf("query: %s\n", query); +#endif + if (mysql_query(pmysql, query)) + dberror(pmysql, "Failed to delete old grants for '%s'.", db); + + for (i = 0; i < lines; i++) + { +#ifdef DEBUG + puts(queries[i]); + putchar('\n'); +#endif + if (mysql_query(pmysql, queries[i])) + dberror(pmysql, "Failed to insert grant line %d.", i + 1); + free(queries[i]); + } + + fprintf(stderr,"Permissions updated\n"); + + return 0; +} + + +int +main(int argc, char *argv[]) +{ + int i; + enum { c_create, c_drop, c_editperm, c_show } command; + MYSQL mysql; + mysql_init(&mysql); + char **dblist, **p; + char *db; + + program_name = argv[0]; + + for (i = 1; i < argc; i++) + if (strcmp(argv[i], "--help") == 0) + return usage(); + for (i = 1; i < argc; i++) + if (strcmp(argv[i], "--help-editperm") == 0) + return usage_editperm(); + for (i = 1; i < argc; i++) + if (strcmp(argv[i], "--version") == 0) + return version(); + + if (argc < 2) + return wrong_use(NULL); + +#ifdef DEBUG + printf("NB NB NB: denne versjonen av programmet er kompilert med -DDEBUG, og\n"); + printf("kan komme til å skrive ut ekstra informasjon. Dette er ikke farlig,\n"); + printf("og programmet bør virke som vanlig.\n"); +#endif + + + /* check that the supplied command is valid */ + + if (strcmp(argv[1], "create") == 0) + command = c_create; + else if (strcmp(argv[1], "drop") == 0) + command = c_drop; + else if (strcmp(argv[1], "editperm") == 0) + command = c_editperm; + else if (strcmp(argv[1], "show") == 0) + command = c_show; + else + return wrong_use("unrecognized command"); /* XXX */ + + /* all other than show requires at least one DATABASE argument. */ + if ((command != c_show) && (argc < 3)) + return wrong_use(NULL); + + read_config_file(); + + /* connect to the database server and select the mysql database */ + if (!mysql_real_connect(&mysql, db_server, db_user, db_passwd, db_name, 0, NULL, 0)) + return dberror(&mysql, "Cannot connect to database server '%s'.", + db_server); + if (mysql_select_db(&mysql, db_name)) + return dberror(&mysql, "Cannot select database '%s'.", db_name); + + if ((command == c_show) && (argc == 2)) { + dblist = list(&mysql); + p = dblist; + while (p && *p) { + show(&mysql, *p); + free(*p); + p++; + } + free(dblist); + } + else { + db = malloc(64); + /* for each supplied database name, perform the requested action */ + for (i = 2; i < argc; i++) { + // HE HE + strncpy(db, argv[i], 32); + db[32] = '\0'; + + if (! (owner(db) || member(db))) { + if (command == c_create) + dberror(NULL,"Unable to create mysql-database '%s'.\n" + "A mysql-database must start with either '%s_' or " + "'groupname_', where groupname is a unix group you are a " + "member of. Type \"groups\" to see which groups you are a " + "member of.\n", + db, getpwuid(getuid())->pw_name); + else + dberror(NULL, "You are not in charge of mysql-database: '%s'. Skipping.", db); + continue; + } + + switch (command) { + case c_create: + // We only check newly created databases. Many old ("unclean") databases are still in use. + if(name_isclean(db)) { + create(&mysql, db); + } else { + dberror(NULL, "Database name '%s' contains invalid characters.\n" + "Only A-Z, a-z, 0-9, _ (underscore) and - (dash) permitted. Skipping.", db); + } + break; + case c_drop: + if(name_isclean(db)) { + drop(&mysql, db); + } else { + dberror(NULL, "Database name '%s' contains invalid characters.\n" + "Only A-Z, a-z, 0-9, _ (underscore) and - (dash) permitted. Skipping.", db); + } + break; + case c_editperm: + if(name_isclean(db)) { + editperm(&mysql, db); + } else { + dberror(NULL, "Database name '%s' contains invalid characters.\n" + "Only A-Z, a-z, 0-9, _ (underscore) and - (dash) permitted. Skipping.", db); + } + break; + case c_show: + if(name_isclean(db)) { + show(&mysql, db); + } else { + dberror(NULL, "Database name '%s' contains invalid characters.\n" + "Only A-Z, a-z, 0-9, _ (underscore) and - (dash) permitted. Skipping.", db); + } + break; + default: + return dberror(NULL, "This point should never be reached!"); + } + } // for + free(db); + } // else + + mysql_reload(&mysql); + mysql_close(&mysql); + + return 0; +} diff --git a/mysql-useradm.c b/mysql-useradm.c new file mode 100644 index 0000000..04bcdca --- /dev/null +++ b/mysql-useradm.c @@ -0,0 +1,367 @@ +/* + * @(#) $Header: /tmp/cvs/mysql-admutils/mysql-useradm.c,v 1.12 2011-09-22 12:17:18 geirha Exp $ + * + * mysql-useradm.c + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mysql-admutils.h" + +int +usage() +{ + printf("Usage: %s COMMAND [USER]...\n", program_name); + printf("Create, delete or change password for the USER(s),\n"); + printf("as determined by the COMMAND. Valid COMMANDs:\n"); + printf("\n"); + printf(" create create the USER(s).\n"); + printf(" delete delete the USER(s).\n"); + printf(" passwd change the MySQL password for the USER(s).\n"); + printf(" show give information about the USERS(s), or, if\n"); + printf(" none are given, all the users you have.\n"); + printf("\n"); + printf("Report bugs to orakel@ntnu.no\n"); + return 0; +} + + +int +is_password_set(MYSQL *pmysql, const char *user) +{ + char query[1024], *end; + MYSQL_RES *res; + int rows; + MYSQL_ROW row; + int check = 0; + + end = strmov(query, "SELECT authentication_string FROM user WHERE user='"); + end += mysql_real_escape_string(pmysql, end, user, strlen(user)); + *end++ = '\''; + *end = '\0'; + + if (mysql_query(pmysql, query)) + dberror(pmysql, "Failed to look up password for user '%s'.", user); + res = mysql_store_result(pmysql); + rows = mysql_num_rows(res); + + if (rows > 1) + { + mysql_free_result(res); + return dberror(NULL, "Query for password for user '%s' gave %d results!", + user, rows); + } + else if (rows < 1) { + mysql_free_result(res); + return -1; + } + + row = mysql_fetch_row(res); + check = (row[0] && (strlen(row[0]) > 0)); + mysql_free_result(res); + + return check; +} + + +int +create(MYSQL *pmysql, const char *user) +{ + char query[1024], *end; + + end = strmov(query, "CREATE USER '"); + end += mysql_real_escape_string(pmysql, end, user, strlen(user)); + end = strmov(end, "'"); + + if (mysql_query(pmysql, query)) + return dberror(pmysql, "Failed to create user '%s'.", user); + + return 0; +} + + +int +delete(MYSQL *pmysql, const char *user) +{ + char query[1024], *end; + + end = strmov(query, "DROP USER '"); + end += mysql_real_escape_string(pmysql, end, user, strlen(user)); + *end++ = '\''; + *end = '\0'; + + if (mysql_query(pmysql, query)) + return dberror(pmysql, "Failed to delete user '%s'.", user); + + return 0; +} + + +int +passwd(MYSQL *pmysql, const char *user) +{ + char prompt[1024]; + char query[1024], *end; + char *password, *confirm_password; + + if (is_password_set(pmysql, user) == -1) /* no such mysql user */ + return dberror(NULL, "User '%s' does not exist." + " You must create it first.\n", user); + + sprintf(prompt, "New MySQL password for user '%s': ", user); + password = getpass(prompt); + confirm_password = strdup(password); + sprintf(prompt, "Retype new MySQL password for user '%s': ", user); + password = getpass(prompt); + if (strcmp(password, confirm_password) != 0) + { + free(confirm_password); + return dberror(NULL, "Sorry, passwords do not match."); + } + free(confirm_password); + + end = strmov(query, "ALTER user '"); + end += mysql_real_escape_string(pmysql, end, user, strlen(user)); + end = strmov(end, "' IDENTIFIED BY '"); + end += mysql_real_escape_string(pmysql, end, password, strlen(password)); + *end++ = '\''; + *end = '\0'; + + if (mysql_query(pmysql, query)) + return dberror(pmysql, "Failed to set new password for user '%s'.", user); + if (mysql_affected_rows(pmysql) != 1) + dberror(NULL, "%d rows affected by password update for user '%s'!", + mysql_affected_rows(pmysql), user); + + fprintf(stderr, "Password updated for user '%s'.\n", user); + return 0; +} + + +int +show(MYSQL *pmysql, const char *user) +{ + switch (is_password_set(pmysql, user)) + { + case -1: + break; + case 0: + printf("User '%s': ", user); + printf("no password set.\n"); + break; + case 1: + printf("User '%s': ", user); + printf("password set.\n"); + break; + } + return 0; +} + + +/* return a list of the user's databases */ +char ** +list(MYSQL *pmysql) +{ + char query[4096], *end; + char **usrgroups, **cp; + MYSQL_RES *res; + int rows, numgroups; + MYSQL_ROW row; + char **userlist; + int i; + struct passwd *p; + + p = getpwuid(getuid()); + + end = strmov(query, "SELECT user FROM user WHERE user='"); + end += mysql_real_escape_string(pmysql, end, p->pw_name, strlen(p->pw_name)); + end = strmov(end, "' OR user LIKE '"); + end += mysql_real_escape_string(pmysql, end, p->pw_name, strlen(p->pw_name)); + end = strmov(end, "\\_%'"); + + numgroups = 0; + + usrgroups = get_group_names(&numgroups); + cp = usrgroups; + while (cp && *cp) { + end = strmov(end, " OR user='"); + end += mysql_real_escape_string(pmysql, end, *cp, strlen(*cp)); + end = strmov(end, "' OR user LIKE '"); + end += mysql_real_escape_string(pmysql, end, *cp, strlen(*cp)); + end = strmov(end, "\\_%'"); + free(*cp); + cp++; + } + free(usrgroups); + +#ifdef DEBUG + printf("about to run query: %s\n", query); +#endif + + if (mysql_query(pmysql, query)) + { + dberror(pmysql, "Failed to look up %s's users.", p->pw_name); + return NULL; + } + res = mysql_store_result(pmysql); + rows = mysql_num_rows(res); + userlist = malloc((rows + 1) * sizeof(char *)); + if (!userlist) + { + dberror(NULL, "%s: Out of memory.\n", program_name); + return NULL; + } + for (i = 0; i < rows; i++) + if ((row = mysql_fetch_row(res))) + { + userlist[i] = strdup(row[0]); + } + + userlist[i] = NULL; + + mysql_free_result(res); + + return userlist; +} + +int +main(int argc, char *argv[]) +{ + int i; + enum { c_create, c_delete, c_passwd, c_show } command; + MYSQL mysql; + mysql_init(&mysql); + char **dblist, **p; + char user[65]; + + program_name = argv[0]; + + for (i = 1; i < argc; i++) + if (strcmp(argv[i], "--help") == 0) + return usage(); + for (i = 1; i < argc; i++) + if (strcmp(argv[i], "--version") == 0) + return version(); + +#ifdef DEBUG + printf("NB NB NB: denne versjonen av programmet er kompilert med -DDEBUG, og\n"); + printf("kan komme til å skrive ut ekstra informasjon. Dette er ikke farlig,\n"); + printf("og programmet bør virke som vanlig.\n"); +#endif + + if (argc < 2) + return wrong_use(NULL); + + /* check that the supplied command is valid */ + + if (strcmp(argv[1], "create") == 0) + command = c_create; + else if (strcmp(argv[1], "delete") == 0) + command = c_delete; + else if (strcmp(argv[1], "passwd") == 0) + command = c_passwd; + else if (strcmp(argv[1], "show") == 0) + command = c_show; + else + return wrong_use("unrecognized command '%s'.", argv[1]); /* XXX */ + + /* all other than show requires at least one USER argument. */ + if ((command != c_show) && (argc < 3)) + return wrong_use(NULL); + + read_config_file(); + + /* connect to the database server and select the mysql database */ + if (!mysql_real_connect(&mysql, db_server, db_user, db_passwd, db_name, 0, NULL, 0)) + return dberror(&mysql, "Cannot connect to database server '%s'.", + db_server); + if (mysql_select_db(&mysql, db_name)) + return dberror(&mysql, "Cannot select database '%s'.", db_name); + + if ((command == c_show) && (argc == 2)) + { + dblist = list(&mysql); + p = dblist; + while (p && *p) + { + show(&mysql, *p); + free(*p); + p++; + } + free(dblist); + } + else { + /* for each supplied database name, perform the requested action */ + + for (i = 2; i < argc; i++) { + strncpy(user, argv[i], 32); + user[33] = '\0'; + if (! (owner(user) || member(user))) + { + if (command == c_create) + dberror(NULL,"Unable to create mysql-user '%s'.\n" + + "A mysql-user must start with either '%s_' or " + "'groupname_', where groupname is a unix group you are a " + "member of. Type \"groups\" to see which groups you are " + "a member of.\n", + user, getpwuid(getuid())->pw_name); + else + dberror(NULL, "You are not in charge of mysql-user: '%s'. Skipping.", user); + continue; + } + + switch (command) + { + case c_create: + if(name_isclean(user)) { + create(&mysql, user); + } else { + dberror(NULL, "User name '%s' contains invalid characters.\n" + "Only A-Z, a-z, 0-9, _ (underscore) and - (dash) permitted. Skipping.", user); + } + break; + case c_delete: + if(name_isclean(user)) { + delete(&mysql, user); + } else { + dberror(NULL, "User name '%s' contains invalid characters.\n" + "Only A-Z, a-z, 0-9, _ (underscore) and - (dash) permitted. Skipping.", user); + } + break; + case c_passwd: + if(name_isclean(user)) { + passwd(&mysql, user); + } else { + dberror(NULL, "User name '%s' contains invalid characters.\n" + "Only A-Z, a-z, 0-9, _ (underscore) and - (dash) permitted. Skipping.", user); + } + break; + case c_show: + if(name_isclean(user)) { + show(&mysql, user); + } else { + dberror(NULL, "User name '%s' contains invalid characters.\n" + "Only A-Z, a-z, 0-9, _ (underscore) and - (dash) permitted. Skipping.", user); + } + break; + default: + fprintf(stderr, "This point should never be reached.\n"); + exit(1); + } + } + } + + reload(&mysql); + mysql_close(&mysql); + + return 0; +} diff --git a/pwfile.c b/pwfile.c new file mode 100644 index 0000000..ce2e573 --- /dev/null +++ b/pwfile.c @@ -0,0 +1,108 @@ +/* + * @($) $Id: pwfile.c,v 1.3 2007-02-27 14:10:08 geirha Exp $ + * + * functions for parsing the config file. + * + */ + +#include +#include +#include +#include +#include +#include +#include "mysql-admutils.h" +#include "pwyacc.h" + +/* the MySQL maximum */ +#define MAXPWCHARS 16 + +/* defaults for configurable values */ +const char* db_user = "root"; +const char* db_server = "localhost"; +const char* db_passwd = NULL; +const char* db_name = "mysql"; + +extern int yyparse(void); +extern FILE *yyin; + +int config_line = 1; + +static FILE *pwfile; +static char *filename = SYSCONFDIR "/mysql-admutils.conf"; + +int +read_config_file(void) +{ + struct rlimit rlim; + int rc; /* return code */ + + /* to stop the user from obtaining the password from a core dump. */ + if (getrlimit(RLIMIT_CORE, &rlim) == -1) { + perror("getrlimit"); + exit(1); + } + rlim.rlim_max = 0; + if (setrlimit(RLIMIT_CORE, &rlim) == -1) { + perror("setrlimit"); + exit(1); + } + + pwfile = fopen(filename, "r"); + if (!pwfile) { + fatal_error("cannot open configuration file %s", filename); + exit(1); + } + + /* we don't need to be SUID anymore. */ + if (seteuid(getuid()) != 0) + perror("seteuid"); + + yyin = pwfile; + rc = yyparse(); + fclose(pwfile); + + return rc; +} + + +int +yywrap(void) +{ + return 1; +} + + +int +yyerror(const char *msg) +{ + fprintf(stderr, "%s:%d: %s\n", filename, config_line, msg); + return 0; +} + + +int +config_set_string_var(int var, const char *value) +{ + assert(value); + switch (var) { + case USER: + db_user = value; + break; + case HOST: + db_server = value; + break; + case PASSWORD: + if (strlen(value) > MAXPWCHARS) { + fprintf(stderr, "%s:%d: password is too long (%d chars > %d chars)\n", + filename, config_line, strlen(value), MAXPWCHARS); + fclose(pwfile); + exit(1); + } + db_passwd = value; + break; + default: + assert(!"We should never get here."); + } + return 0; +} diff --git a/pwlex.l b/pwlex.l new file mode 100644 index 0000000..457ef74 --- /dev/null +++ b/pwlex.l @@ -0,0 +1,47 @@ +/* + * @(#) $Id: pwlex.l,v 1.1.1.1 2001-11-25 00:41:16 lkarsten Exp $ + * + * lex source for the configuration file + * + */ + +%{ +#include +#include +#include "pwyacc.h" + +extern int config_line; +extern int yylval; +%} + +%% + +#.*\n config_line++; + +set { yylval = SET; return(SET); } +host { yylval = HOST; return(HOST); }; +user { yylval = USER; return(USER); }; +password { yylval = PASSWORD; return(PASSWORD); } + +\"[^"]*\" { + if (yytext[yyleng - 1] == '\\') + yymore(); + else + yytext[yyleng - 1] = '\0'; + yylval = (int)strdup(yytext + 1); + return(STRING); + }; + +; return(';'); + +[^ \t\n\;]* { + yylval = (int)strdup(yytext); + return(STRING); + } + +[ \t] ; + +\n config_line++; + +%% + diff --git a/pwyacc.y b/pwyacc.y new file mode 100644 index 0000000..1a39886 --- /dev/null +++ b/pwyacc.y @@ -0,0 +1,33 @@ +/* + * @(#) $Id: pwyacc.y,v 1.1.1.1 2001-11-25 00:41:16 lkarsten Exp $ + * + * yacc source for parsing the configuration file + * + */ + +%{ +int yyerror(const char *msg); +int yylex(void); +int config_set_string_var(int var, const char *value); +%} + +%token SET HOST USER PASSWORD STRING + +%% + +input: ; +input: statement input ; + +statement: SET variable STRING ';' + { + $$ = config_set_string_var($2, (const char *)$3); + } + +; + +variable: USER | PASSWORD | HOST + { $$ = $1; } + ; + +%% + diff --git a/stamp-h.in b/stamp-h.in new file mode 100644 index 0000000..9788f70 --- /dev/null +++ b/stamp-h.in @@ -0,0 +1 @@ +timestamp