2014-03-13 169 views
2

我想做一個suid應用程序,它只會執行位於受限文件夾中的ruby腳本。我試圖用realpath(3)來做到這一點,但它只返回路徑的第一部分。下面是我的代碼...如何將相對路徑轉換爲C中的絕對路徑?

#include <stdio.h> 
#include <stdlib.h> 
#include <sys/types.h> 
#include <unistd.h> 
#include <string.h> 

#define SUEXEC_STR_LEN 2048 
#define RUBY_APP "/usr/bin/ruby" 
#define DIRECTORY_SEPARATOR "/" 

static void safepath(const char *path_in, char * path_out, int outlen) { 
    realpath(path_in, path_out); 
} 

int main (int argc, char *argv[]) 
{ 
    char cmd[SUEXEC_STR_LEN]; 
    char path_out[SUEXEC_STR_LEN]; 
    char path_in[SUEXEC_STR_LEN]; 

    char *cp = &cmd[0]; 

    strncpy(cp, RUBY_APP, SUEXEC_STR_LEN - 1); 

    strncpy(path_in, DIRECTORY_SEPARATOR, SUEXEC_STR_LEN - 1); 
    strncat(path_in,argv[1],SUEXEC_STR_LEN - 1); 

    safepath(path_in,path_out,SUEXEC_STR_LEN - 1); 

    printf("path_in=%s path_out=%s\n",path_in,path_out); 

    setuid(0); 
    // system(cmd); 

    return 0; 
} 

這是我得到

[email protected]:/root/src# ./a.out foo/bar/../test 
path_in=/foo/bar/../test path_out=/foo 

這一結果的一個例子的結果,我想

[email protected]:/root/src# ./a.out foo/bar/../test 
path_in=/foo/bar/../test path_out=/foo/test 
+0

我覺得這裏的問題是,'真實路徑()'預計的路徑實際存在,因爲它也通告它解析符號鏈接,它不可能做,如果它只是操縱字符串。 – Emmet

+0

@Emmet,你是​​對的。那麼我應該使用什麼呢? –

+0

http://stackoverflow.com/questions/11034002/how-to-get-absolute-path-of-file-or-directory-that-does-not-exist自己執行字符串操作。 – timrau

回答

1

您應該檢查realpath()的回報價值。正如在其man page

返回值
描述如果沒有錯誤,真實路徑()返回一個指針resolved_pa​​th。

否則它返回一個NULL指針,並且數組resolved_pa​​th的內容未定義。全局變量errno被設置爲指示錯誤。

在其手冊頁的錯誤部分

此外,

ENOENT指定的文件不存在。

因此,如果確實是在文件系統中沒有/foo/testrealpath()應該返回NULL和輸出是不確定的。

+0

這很有幫助,但只會揭示realpath()不能用於解析不存在的文件。它不能解決文件不存在時將相對路徑轉換爲絕對路徑的問題。 –

1

所以,這裏是你會如何去了解它在ç的Linux一個工作草圖。這是一種快速入侵,我並不認爲它是典型的代碼,高效的等等。它(ab)使用PATH_MAX,使用「壞」字符串函數,並且可能會泄漏內存,吃掉你的貓,並且存在段錯誤等等。 它打破,你會保持兩個部分。

其基本思想是通過給定的路徑,使用「/」作爲分隔符將其分解爲「單詞」。然後,通過列表,將「單詞」推到堆棧上,但忽略空或「。」,如果「..」則彈出,然後從底部開始序列化堆棧並在其間堆積一個帶斜槓的字符串。

#include <stdio.h> 
#include <string.h> 
#include <stdlib.h> 
#include <linux/limits.h> 

typedef struct stack_s { 
    char *data[PATH_MAX]; 
    int top; 
} stack_s; 

void stack_push(stack_s *s, char *c) { 
    s->data[s->top++] = c; 
} 

char *stack_pop(stack_s *s) { 
    if(s->top <= 0) { 
     return NULL; 
    } 
    s->top--; 
    return s->data[s->top]; 
} 

// DANGER! DANGER! Returns malloc()ed pointer that you must free() 
char *stack_serialize(stack_s *s) { 
    int i; 
    char *buf; 
    int len=1; 

    for(i=0; i<s->top; i++) { 
     len += strlen(s->data[i]); 
     len++; // For a slash 
    } 
    buf = malloc(len); 
    *buf = '\0'; 
    for(i=0; i<s->top-1; i++) { 
     strcat(buf, s->data[i]); 
     strcat(buf, "/"); 
    } 
    strcat(buf, s->data[i]); 
    return buf; 
} 

// DANGER! DANGER! Returns malloc()ed pointer that you must free() 
char *semicanonicalize(char *src) { 
    char *word[PATH_MAX] = {NULL}; 
    int w=0; 
    int n_words; 

    char *buf; 
    int len; 
    char *p, *q; 

    stack_s dir_stack = {{NULL},0}; 

    // Make a copy of the input string: 
    len = strlen(src); 
    buf = strdup(src); 

    // Replace slashes with NULs and record the start of each "word" 
    q = buf+len; 
    word[0]=buf; 
    for(p=buf,w=0; p<q; p++) { 
     if(*p=='/') { 
      *p = '\0'; 
      word[++w] = p+1; 
     } 
    } 
    n_words=w+1; 

    // We push w[0] unconditionally to preserve slashes and dots at the 
    // start of the source path: 
    stack_push(&dir_stack, word[0]); 

    for(w=1; w<n_words; w++) { 
     len = strlen(word[w]); 
     if(len == 0) { 
      // Must've hit a double slash 
      continue; 
     } 
     if(*word[w] == '.') { 
      if(len == 1) { 
       // Must've hit a dot 
       continue; 
      } 
      if(len == 2 && *(word[w]+1)=='.') { 
       // Must've hit a '..' 
       (void)stack_pop(&dir_stack); 
       continue; 
      } 
     } 
     // If we get to here, the current "word" isn't "", ".", or "..", so 
     // we push it on the stack: 
     stack_push(&dir_stack, word[w]); 
    } 

    p = stack_serialize(&dir_stack); 
    free(buf); 
    return p; 
} 


int main(void) 
{ 
    char *in[] = { "/home/emmet/../foo//./bar/quux/../.", 
        "../home/emmet/../foo//./bar/quux/../.", 
        "./home/emmet/../foo//./bar/quux/../.", 
        "home/emmet/../foo//./bar/quux/../." 
    }; 
    char *out; 
    for(int i=0; i<4; i++) { 
     out = semicanonicalize(in[i]); 
     printf("%s \t->\t %s\n", in[i], out); 
     free(out); 
    } 
    return 0; 
} 
+0

我喜歡這個問題的邏輯方法。我現在使用的黑客實際上是無法讀取的。儘管如此,我仍會將其發佈用於存檔目的。 –

0

這是我用作解決問題的代碼。它可能還有一些bug,並且它沒有檢查outlen參數以避免段錯誤和其他醜陋,但它似乎完成了工作。

#include <stdio.h> 
#include <stdlib.h> 
#include <sys/types.h> 
#include <unistd.h> 
#include <string.h> 
#include <linux/limits.h> 

#define SUEXEC_STR_LEN 2048 
#define RUBY_APP "/usr/bin/ruby" 
#define DIRECTORY_SEPARATOR "/" 
#define RUBY_EXT ".rb" 

#define SERVICES_BASE_PATH "/path/to/ruby/services" 

static inline int isDirSeparator(const char c) { return (c == '/' || c == '\\'); } 

static void safepath(const char *path_in, char * path_out, int outlen) 
{ 
    char *dirs[PATH_MAX]; 
    int depth = 0; 
    char *dstptr = path_out; 
    const char *srcptr = path_in; 

    *dstptr++ = DIRECTORY_SEPARATOR[0]; 
    dirs[0] = dstptr; 
    dirs[1] = NULL; 
    depth++; 

    while (1) { 
     if ((srcptr[0] == '.') && isDirSeparator(srcptr[1])) { 
      srcptr += 2; 
     } else if (srcptr[0] == '.' && srcptr[1] == '.' && isDirSeparator(srcptr[2])) { 
      if (depth > 1) { 
       dirs[depth] = NULL; 
       depth--; 
       dstptr = dirs[depth-1]; 
      } else { 
       dstptr = dirs[0]; 
      } 
      srcptr += 3; 
     } else if (srcptr[0] == '.' && srcptr[1] == '.' && srcptr[2] == 0) { 
      if (depth == 1) { 
       srcptr += 2; 
      } else { 
       depth--; 
       dstptr = dirs[depth-1]; 
       srcptr += 2; 
      } 
     } else { 
      while (!isDirSeparator(srcptr[0]) && srcptr[0]) { 
       *dstptr++ = *srcptr++; 
      } 
      if (srcptr[0] == 0) { 
       if (dstptr != dirs[0] && isDirSeparator(dstptr[-1])) { 
        dstptr[-1] = 0; 
       } 
       dstptr[0] = 0; 
       return; 
      } else if (isDirSeparator(srcptr[0])) { 
       if (dstptr == dirs[0]) { 
        srcptr++; 
       } else { 
        *dstptr++ = *srcptr++; 
        dirs[depth] = dstptr; 
        depth++; 
       } 
       while (isDirSeparator(srcptr[0]) && srcptr[0]) { 
        srcptr++; 
       } 
      } else { 
       path_out[0] = 0; 
       return; 
      } 
     } 
    } 
} 

int main (int argc, char *argv[]) 
{ 
    int ret; 
    char cmd[SUEXEC_STR_LEN]; 
    char path_out[SUEXEC_STR_LEN]; 
    char path_in[SUEXEC_STR_LEN]; 

    char *cp = &cmd[0]; 


    if (argc < 2) { 
     fprintf(stderr,"usage: %s <service>\n",argv[0]); 
     return 1; 
    } 
    strncpy(cp, RUBY_APP, SUEXEC_STR_LEN - 1); 

    strncpy(path_in, DIRECTORY_SEPARATOR, SUEXEC_STR_LEN - 1); 
    strncat(path_in,argv[1],SUEXEC_STR_LEN - 1); 

    safepath(path_in,path_out,SUEXEC_STR_LEN - 1); 

    //printf("path_in=%s path_out=%s\n",path_in,path_out); 

    strncat(cmd," ",SUEXEC_STR_LEN - (1+sizeof(RUBY_EXT))); 

    strncat(cmd,SERVICES_BASE_PATH,SUEXEC_STR_LEN - (1+sizeof(RUBY_EXT))); 
    strncat(cmd,path_out,SUEXEC_STR_LEN - (1+sizeof(RUBY_EXT))); 
    strncat(cmd,RUBY_EXT,SUEXEC_STR_LEN - 1); 

    setuid(0); 
    ret = system(cmd); 
    if (ret == -1) { 
     return ret; 
    } 
    ret = WEXITSTATUS(ret); 
    return ret; 
} 
相關問題