easyfind-client (Mouette)

Discuss development on Bubba
Gordon
Posts: 1339
Joined: 10 Aug 2011, 03:18

easyfind-client (Mouette)

Post by Gordon » 08 Jan 2016, 08:01

Hi all.

I've been looking into Mouette's easyfind-client and while I like it a lot I also think there is a missed opportunity in it as well. What I found is that I cannot use it as is, because like the original software it depends on the key being accessible through the kernel cmdline. But my (sakaki) kernel overwrites the cmdline, meaning that this value is not there. I would also have liked it better if it had been a plug-in replacement for the old easyfind routines, but this client stores its data in a different file and a different format.

This can all be fixed and in fact I have already started rewriting the method to retrieve the key value, reading it directly from the onboard flash memory. Running as an individual application I've verified that this method returns the key on my B3 correctly. What I was wondering though is whether this same method will work on a B2 as well? Does the B2 use the same flash structure to load the key on the kernel command line? If so, would someone who owns a B2 be willing to compile and run this bit of code and see if it returns the key value. Of course this would work best if done by someone who has an alternative method to access this key value so he/she can compare the results.

wm.bubba
Posts: 82
Joined: 11 May 2009, 12:58

Re: easyfind-client (Mouette)

Post by wm.bubba » 08 Jan 2016, 12:55

Hi Gordon,

I have a couple of B2's, and willing to give it a try.
I think I also know how to read the key directly. Just need to dig my old notes out.

MouettE
Site admin
Posts: 263
Joined: 06 Oct 2011, 19:45

Re: easyfind-client (Mouette)

Post by MouettE » 08 Jan 2016, 13:43

Gordon wrote:This can all be fixed and in fact I have already started rewriting the method to retrieve the key value, reading it directly from the onboard flash memory.
I eagerly wait for a pull request :D
Gordon wrote:What I was wondering though is whether this same method will work on a B2 as well? Does the B2 use the same flash structure to load the key on the kernel command line? If so, would someone who owns a B2 be willing to compile and run this bit of code and see if it returns the key value.
It should work on the B2 as well, the flash parameters are obviously different but the method is the same. I have a b2 for test purpose so I can also test your code.

Gordon
Posts: 1339
Joined: 10 Aug 2011, 03:18

Re: easyfind-client (Mouette)

Post by Gordon » 08 Jan 2016, 13:52

Perfect!

Code below. Nothing fancy. Just run gcc on it and execute the resulting a.out executable.

Code: Select all

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <mtd/mtd-user.h>
 
int main()
{
    int fd = open("/dev/mtd1", O_RDWR);
    int offset=0;
    ssize_t buflen=3;
    char buf[64];
    char* key;
    char* keyval;

    while (buflen>0){
        offset+=buflen;
	if (buflen<64) offset++;
        lseek(fd, offset, SEEK_SET);
        read(fd, buf, sizeof(buf));
	if (buflen==64 && strlen(buf)==0){
            offset++;
            lseek(fd, offset, SEEK_SET);
            read(fd, buf, sizeof(buf));
        }
        buflen=strlen(buf);
        key=strstr(buf,"key=");
        if (key!=NULL && key==buf){
            keyval = malloc(strlen(buf)-3);
            strcpy(keyval, buf+4);
            keyval[strlen(buf)-4] = '\0';
        }
    }
    printf("%s\n",keyval);
    return 0;
}

wm.bubba
Posts: 82
Joined: 11 May 2009, 12:58

Re: easyfind-client (Mouette)

Post by wm.bubba » 08 Jan 2016, 15:25

Program compiled, and run:

Code: Select all

user@b2:~/temp/ef$ ll
total 20
drwxr-xr-x 2 user users 4096 Jan  8 20:21 .
drwxr-xr-x 6 user users 4096 Jan  8 20:14 ..
-rwxr-xr-x 1 user users 6473 Jan  8 20:21 a.out
-rw-r--r-- 1 user users  847 Jan  8 18:55 ef.c
user@b2:~/temp/ef$ ./a.out

user@b2:~/temp/ef$ 
but as you can see nothing is returned.

Gordon
Posts: 1339
Joined: 10 Aug 2011, 03:18

Re: easyfind-client (Mouette)

Post by Gordon » 08 Jan 2016, 16:10

Which means it doesn't work. Either because the u-boot environment is in a different partition than /dev/mtd1, or /dev/mtd1 is inaccessible, or "key" is named differently inside it. That is a bit of a disappointment, especially because I'm kind of running in the dark here in regards to the B2. But maybe someone else can figure out what needs to be changed to make this work on a B2?

MouettE
Site admin
Posts: 263
Joined: 06 Oct 2011, 19:45

Re: easyfind-client (Mouette)

Post by MouettE » 08 Jan 2016, 16:18

I will have a look and I'm pretty sure I'll find a working solution.

sakaki
Posts: 172
Joined: 15 Aug 2014, 11:20

Re: easyfind-client (Mouette)

Post by sakaki » 08 Jan 2016, 17:45

Hi,

FWIW on our (rev 2 hardware) B2, running the current Gentoo live-USB for B2, it is /dev/mtd0 defined in /etc/fw_env.config, thus:

Code: Select all

# MTD definition for Bubba|2
# MTD device name       Device offset   Env. size       Flash sector size
/dev/mtd0               0x050000        0x002000        0x010000
/dev/mtd0               0x060000        0x002000        0x010000
Running Gordon's program (substituting of course /dev/mtd0, rather than /dev/mtd1) returns a blank line. However, I can extract the key with:

Code: Select all

b2 mtdtest # strings /dev/mtd0 | grep -oP 'key=[^$]+'
key=dKMS<... omitted for privacy ...>CWp/6Eo=
(the same value is also returned by fw_printenv).

I haven't reflashed U-boot or changed the U-boot environment.

Hope that helps, sakaki

Gordon
Posts: 1339
Joined: 10 Aug 2011, 03:18

Re: easyfind-client (Mouette)

Post by Gordon » 09 Jan 2016, 11:45

Explanation of the code:

What it does is start at offset 3 (ignoring an integer value at offset 0 that I did not bother to investigate what it is for) and read data in chunks of a maximum of 64 bytes. How many bytes are returned depends on whether there is a \0 value within the 64 byte range. The cycle stops when the buffer starts with the 4 character sequence "key=" or when it hits two consecutive \0 characters (in which case the buffer length is 0 on the second read).

The problem here is that with the B2:
1. the u-boot environment is not in /dev/mtd1
2. is not at offset 3

So it might work if you change the line 'ssize_t buflen=3;' to read 'ssize_t buflen=50003;' (or 60003)


Edit: not true. Stopping the cycle the moment key was found was what was intended at first. It will in fact read the complete environment, i.e. to the point where it finds two consecutive '\0' characters.
Last edited by Gordon on 11 Jan 2016, 13:08, edited 1 time in total.

sakaki
Posts: 172
Joined: 15 Aug 2014, 11:20

Re: easyfind-client (Mouette)

Post by sakaki » 10 Jan 2016, 12:53

Made the suggested changes, but it still didn't work. I think the program below should do the trick however - it appears to print the key correctly on our B3s and (rev 2) B2, but YMMV (is there anyone out there with a rev 1 B2 who could try this out? paulchany?).

Compile with gcc and then run a.out (as root, to ensure you have read access to /dev/mtd{0,1}):

Code: Select all

/* Search for a U-boot variable in B2/B3's MTD memory and print it
   By sakaki, 2016 - public domain - NO WARRANTY */

#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>

const char* PATTERN = "\0key=";
const int PATLEN = 5;

int main()
{
    int fd;
    off_t envlen = 0x010000;
    off_t envstart = 0x000000;
    char* data, *p, *end;
    int f;

    fd = open("/dev/mtd1", O_RDONLY);
    if (fd == -1) {
        /* looks like we are running on a B2 */
        fd = open("/dev/mtd0", O_RDONLY);
        envlen = 0x002000;
        envstart = 0x060000;
    }
    if (fd == -1) return EXIT_FAILURE;
    /* load whole file into memory, the U-boot env area is tiny */
    lseek(fd, envstart, SEEK_SET);
    data = malloc(envlen);
    read(fd, data, envlen);
    end=data+envlen;

    for (f=0, p=data; f<PATLEN && p<end; p++)
        f = (*p == PATTERN[f] ? f+1 : 0);
    if (f == PATLEN) {
        do write(STDOUT_FILENO, p, 1); while (++p<end && *p);
        write(STDOUT_FILENO, "\n", 1);
    } else write(STDOUT_FILENO, "FAILED\n", 7);
    
    free(data);
    close(fd);
    return (f == PATLEN ? EXIT_SUCCESS : EXIT_FAILURE);
}
Best, sakaki

Gordon
Posts: 1339
Joined: 10 Aug 2011, 03:18

Re: easyfind-client (Mouette)

Post by Gordon » 10 Jan 2016, 17:29

Should it have been 0x60003 in stead of 60003? I suppose you did try that variation. Don't know why this would go wrong on the B2. In fact I found that if ithe code I posted did not catch the key it should have returned a segmentation fault on account of the printf command at the end trying to write an uninitialised value. Not returning anything at all should therefore never have happened.

While good to have something that appears to be working, I do dislike the part of needing to allocate 8k of memory for reading the u-boot env. The objective (at least from my part) is to use as little memory as possible. And of course integrate it with Mouette's code, replacing the current routine to read the key from cmdline.

sakaki
Posts: 172
Joined: 15 Aug 2014, 11:20

Re: easyfind-client (Mouette)

Post by sakaki » 11 Jan 2016, 08:10

From running gdb on your code, what appears to be happening is this. Consider the lines:

Code: Select all

read(fd, buf, sizeof(buf));
followed by

Code: Select all

buflen=strlen(buf);
and then ultimately

Code: Select all

offset+=buflen
if (buflen<64) offset++;
lseek(fd, offset, SEEK_SET);
in the case where an environment string with length > sizeof(buf) (64 bytes) is encountered (many of them are).

In such a situation, the value returned by strlen is essentially a random integer >=64. In my case it returns the value 70 (decimal) (depends what's on the stack after the last byte of buf, which is a bit of a lucky dip). What then happens is that the 70 gets added to offset, and as luck would have it, this causes it to seek past the (start of the) "key=..." string. The (indeterminate) value of keyval is then printed - this happens to be a pointer to zero memory on the B2 (at least with my compiler settings), so there is no segfault (running this on a PC (on a text file which is missing the target string) does segfault I agree, except with -O3 and similar, when it prints garbage).

Best, sakaki

Gordon
Posts: 1339
Joined: 10 Aug 2011, 03:18

Re: easyfind-client (Mouette)

Post by Gordon » 11 Jan 2016, 08:49

Good find.

It should be possible to fix this by using:

Code: Select all

    char buf[65];
    buf[64]='\0';
And hardcode the value 64 where sizeof(buf) was used

Funny that I did not hit the issue with random characters following the read buffer on the B3. And I'm certain about that, because I had some additional print lines in there at first, dumping the complete environment to screen.

BTW I found what the first four bytes in the environment are for. It's a CRC32 checksum.

wm.bubba
Posts: 82
Joined: 11 May 2009, 12:58

Re: easyfind-client (Mouette)

Post by wm.bubba » 11 Jan 2016, 09:28

Making the changes outlined (see code below), returns the correct key on both my B2's.

Code: Select all

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <mtd/mtd-user.h>
 
int main()
{
    int fd = open("/dev/mtd0", O_RDWR);
    int offset=0;
    ssize_t buflen=0x60003;
    char buf[65];
    buf[64]='\0';
    char* key;
    char* keyval;

    while (buflen>0){
        offset+=buflen;
   if (buflen<64) offset++;
        lseek(fd, offset, SEEK_SET);
        read(fd, buf, 64);
   if (buflen==64 && strlen(buf)==0){
            offset++;
            lseek(fd, offset, SEEK_SET);
            read(fd, buf, 64);
        }
        buflen=strlen(buf);
        key=strstr(buf,"key=");
        if (key!=NULL && key==buf){
            keyval = malloc(strlen(buf)-3);
            strcpy(keyval, buf+4);
            keyval[strlen(buf)-4] = '\0';
        }
    }
    printf("%s\n",keyval);
    return 0;
}

Gordon
Posts: 1339
Joined: 10 Aug 2011, 03:18

Re: easyfind-client (Mouette)

Post by Gordon » 11 Jan 2016, 10:39

Aha. Progress.

So then this should work for both platforms:

Code: Select all

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <mtd/mtd-user.h>


char* getkey() {
#if defined(__powerpc__) || defined(__ppc__) || defined(__PPC__)
    int fd = open("/dev/mtd0", O_RDWR);
    int offset=0x60000;
#else
    int fd = open("/dev/mtd1", O_RDWR);
    int offset=0;
#endif
    ssize_t buflen=3;
    char buf[65];
    char* key;
    char* keyval="";

    buf[64]='\0';
    while (buflen>0 && offset<0x20000){
        offset+=buflen;
        if (buflen<64) offset++;
        lseek(fd, offset, SEEK_SET);
        read(fd, buf, 64);
        if (buflen==64 && strlen(buf)==0){
            offset++;
            lseek(fd, offset, SEEK_SET);
            read(fd, buf, 64);
        }
        buflen=strlen(buf);
        key=strstr(buf,"key=");
        if (key!=NULL && key==buf){
            keyval = malloc(strlen(buf)-3);
            strcpy(keyval, buf+4);
            keyval[strlen(buf)-4] = '\0';
        }
    }
    return keyval;
}

int main() {
    printf("%s\n",getkey());
}
(and be a drop in replacement for the current procedure that reads the key from cmdline)

Post Reply