summaryrefslogtreecommitdiffstats
path: root/login-utils/islocal.c
diff options
context:
space:
mode:
authorJames Youngman2008-04-12 13:53:07 +0200
committerKarel Zak2008-04-14 13:12:14 +0200
commit8e0259728e6b7c447d795898f60a9281053c8303 (patch)
tree4a6c4c13bb423513fefb035df70f48c1f46f1866 /login-utils/islocal.c
parentdocs: cleanup README.devel, add note about coding style and Signed-off-by (diff)
downloadkernel-qcow2-util-linux-8e0259728e6b7c447d795898f60a9281053c8303.tar.gz
kernel-qcow2-util-linux-8e0259728e6b7c447d795898f60a9281053c8303.tar.xz
kernel-qcow2-util-linux-8e0259728e6b7c447d795898f60a9281053c8303.zip
login: rewrite is_local() to remove limits on line length
Signed-off-by: James Youngman <jay@gnu.org> Signed-off-by: Karel Zak <kzak@redhat.com>
Diffstat (limited to 'login-utils/islocal.c')
-rw-r--r--login-utils/islocal.c105
1 files changed, 84 insertions, 21 deletions
diff --git a/login-utils/islocal.c b/login-utils/islocal.c
index 297698041..762a4e990 100644
--- a/login-utils/islocal.c
+++ b/login-utils/islocal.c
@@ -12,7 +12,10 @@
1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
- added Native Language Support
-
+
+ 2008-04-06 James Youngman, jay@gnu.org
+ - Completely rewritten to remove assumption that /etc/passwd
+ lines are < 1024 characters long. Also added unit tests.
*/
@@ -24,29 +27,89 @@
#include "pathnames.h"
#include "islocal.h"
-#define MAX_LENGTH 1024
-
-int
-is_local(char *user)
+static int
+is_local_in_file(const char *user, const char *filename)
{
- FILE *fd;
- char line[MAX_LENGTH];
int local = 0;
- size_t len;
-
- if(!(fd = fopen(_PATH_PASSWD, "r"))) {
- fprintf(stderr,_("Can't read %s, exiting."),_PATH_PASSWD);
- exit(1);
- }
-
- len = strlen(user);
- while(fgets(line, MAX_LENGTH, fd)) {
- if(!strncmp(line, user, len) && line[len] == ':') {
- local = 1;
- break;
- }
+ size_t match;
+ int chin, skip;
+ FILE *f;
+
+ if (NULL == (f=fopen(filename, "r")))
+ return -1;
+
+ match = 0u;
+ skip = 0;
+ while ((chin = getc(f)) != EOF) {
+ if (skip) {
+ /* Looking for the start of the next line. */
+ if ('\n' == chin) {
+ /* Start matching username at the next char. */
+ skip = 0;
+ match = 0u;
+ }
+ } else {
+ if (':' == chin) {
+ if (0 == user[match]) {
+ local = 1; /* Success. */
+ /* next line has no test coverage, but it is
+ * just an optimisation anyway. */
+ break;
+ } else {
+ /* we read a whole username, but it is
+ * the wrong user. Skip to the next
+ * line. */
+ skip = 1;
+ }
+ } else if ('\n' == chin) {
+ /* This line contains no colon; it's malformed.
+ * No skip since we are already at the start of
+ * the next line. */
+ match = 0u;
+ } else if (chin != user[match]) {
+ /* username does not match. */
+ skip = 1;
+ } else {
+ ++match;
+ }
+ }
}
- fclose(fd);
+ fclose(f);
return local;
}
+int
+is_local(const char *user)
+{
+ int rv;
+ if ((rv = is_local_in_file(user, _PATH_PASSWD)) < 0) {
+ perror(_PATH_PASSWD);
+ fprintf(stderr, _("Failed to open %s for reading, exiting."),
+ _PATH_PASSWD);
+ exit(1);
+ } else {
+ return rv;
+ }
+}
+
+#if MAIN_TEST_ISLOCAL
+int
+main (int argc, char *argv[])
+{
+ if (argc < 2) {
+ fprintf(stderr, "No test passwd file was specified.\n");
+ return 1;
+ } else {
+ int i;
+ for (i = 2; i < argc; i++) {
+ const int rv = is_local_in_file(argv[i], argv[1]);
+ if (rv < 0) {
+ perror(argv[1]);
+ return 2;
+ }
+ printf("%d:%s\n", rv, argv[i]);
+ }
+ return 0;
+ }
+}
+#endif