2022-01-21

一冤馏、 D-Bus簡介

D-Bus是一種高效践付、易用的進(jìn)程間通信方式.
D-Bus分為兩種:system bus(系統(tǒng)總線)威兜,用于系統(tǒng)(Linux)與用戶程序之間進(jìn)行通信和消息的傳遞芙粱;session bus(會(huì)話總線)狼渊,用于用戶程序之間進(jìn)行通信.

D-Feet是一個(gè)查看和調(diào)用D-Bus接口的圖形化工具.UOS下安裝命令sudo apt install d-feet.
D-Bus官網(wǎng)
D-Feet的使用
D-Feet Wiki

二. 背景介紹

1. libsysted sd-bus簡介

一個(gè)輕量級(jí)的D-Bus庫. 我們用它來演示如何開發(fā)一個(gè)簡答的系統(tǒng)D-Bus程序.
編譯依賴:pkg-config libsystemd-dev  libsystemd0 
編譯時(shí)頭文件和動(dòng)態(tài)庫: 運(yùn)行 `pkg-config --cflags --libs libsystemd` 獲得.

2. D-bus接口org.freedesktop.PolicyKit1.Authority

org.freedesktop.PolicyKit1 是polkit的系統(tǒng)dbus接口.
通過此dbus接口熟史,可以驗(yàn)證權(quán)限馁害,并通過與用戶交互來獲取權(quán)限.
下面是一個(gè)通過D-Feet使用org.freedesktop.PolicyKit1.Authority CheckAuthorization dbus方法來驗(yàn)證進(jìn)程deepin-devicemanager是否具備org.gnome.gparted行為的權(quán)限的例子.
(1) 通過pkaction查看權(quán)限

pkaction --action-id org.gnome.gparted --verbose    

可以知道 org.gnome.gparted 需要 auth_admin,即管理員權(quán)限.

(2) 打開一個(gè)應(yīng)用程序.這里打開了deepin-devicemanager,其pid為8798.

(3) D-Feet調(diào)用org.freedesktop.PolicyKit1.Authority CheckAuthorization接口

("unix-process", {"pid":GLib.Variant('u', 4574), "start-time":GLib.Variant('t', 0)}),
"org.gnome.gparted",
{},
1, 
"timeout=600"

[圖片上傳失敗...(image-d3d50b-1642718751398)]
polkit會(huì)彈出認(rèn)證窗口來與用戶交互蹂匹,獲取授權(quán).
若用戶輸入正確密碼碘菜,返回 (True, False, {}),表示進(jìn)程deepin-devicemanager擁有了執(zhí)行org.gnome.gparted action的權(quán)限.
若用戶關(guān)閉驗(yàn)證框,返回 (False, False, {'polkit.dismissed': 'true'}),標(biāo)示獲取權(quán)限失敗.

詳見org.freedesktop.PolicyKit1.Authority Interface

3. UOS系統(tǒng)白名單機(jī)制

UOS系統(tǒng)通過白名單機(jī)制放行一些系統(tǒng)級(jí)的可執(zhí)行程序忍啸,白名單中的程序擁有較高的權(quán)限仰坦,被允許直接放行.

二. 一個(gè)系統(tǒng)D-Bus示例程序,調(diào)用此D-Bus接口時(shí)會(huì)對(duì)調(diào)用者進(jìn)行鑒權(quán)

1. 自定義一個(gè)polkit action

action對(duì)應(yīng)了用戶執(zhí)行操作的polkit權(quán)限要求计雌,添加的基本步驟如下
(1)根據(jù)要開發(fā)的系統(tǒng)dbus功能悄晃,新建policy文件.
policy文件是xml格式,例如actions/net.poettering.Calculator.policy.
填入圖標(biāo)凿滤、action id妈橄,描述,polkit窗口消息等.
最主要是定義default值

allow_any:      任意的客戶端都可以
allow_inactive: 遠(yuǎn)程交互客戶端 (SSH, VNC, etc.)
allow_active:   直接登錄的TTY或X display客戶端

每個(gè)allow_***可以設(shè)置為以下的值:

表頭 表頭
no 不通過
yes 通過
auth_self 以當(dāng)前session用戶身份來驗(yàn)證
auth_admin 以管理員身份來驗(yàn)證
auth_self_keep 以當(dāng)前session用戶身份來驗(yàn)證鸭巴,但僅保持一定時(shí)間驗(yàn)證有效 (例如5分鐘無需在此驗(yàn)證)
auth_admin_keep 以管理員用戶身份來驗(yàn)證眷细,但僅保持一定時(shí)間驗(yàn)證有效 (例如5分鐘無需在此驗(yàn)證)

(2) 復(fù)制policy文件到 /usr/share/polkit-1/actions.
(3) policy文件添加/usr/share/polkit-1/actions后使用pkaction檢查是否生效.

pkaction --action-id net.poettering.Calculator --verbose

同樣的,你可以通過D-Feet調(diào)用org.freedesktop.PolicyKit1 D-Bus接口看下添加的action效果.

2. D-Bus設(shè)置

system.d/net.poettering.Calculator.conf復(fù)制到 /etc/dbus-1/system.d/文件夾下鹃祖,允許任何程序調(diào)用net.poettering.Calculator D-Bus接口

3. 系統(tǒng)D-Bus 程序

<details>
<summary>展開查看 代碼test.c</summary>
<pre><code>
#include <stdio.h>

include <stdlib.h>

include <errno.h>

include <systemd/sd-bus.h>

include <stdio.h>

include <unistd.h>

include <stdlib.h>

include <errno.h>

include <systemd/sd-bus.h>

define MAX_BUF_SIZE 1024

define SECURITY_WHILTELIST_FILE "/var/lib/deepin/deepin_security_verify.whitelist"

/* 檢查程序是否在白名單中
讀取白名單文件中的列表并比對(duì)
*/
static int has_in_whitelist(const char *path)
{
int ret = 0;
char buf[MAX_BUF_SIZE] = {0};

FILE *fr = fopen(SECURITY_WHILTELIST_FILE, "r");
if (!fr) {
    return ret;
}

while (!feof(fr)) {
    memset(buf, 0, MAX_BUF_SIZE);
    char* c = fgets(buf, MAX_BUF_SIZE, fr);
    if (c == NULL) {
        break;
    }

    // trim '\n'
    size_t len = strlen(buf);
    if (buf[len-1] == '\n') {
        buf[len-1] = '\0';
    }
    if (strcmp(buf, path) == 0) {
        ret = 1;
        break;
    }
}

fclose(fr);
return ret;

}

/* 根據(jù)pid 獲取 發(fā)送進(jìn)程的路徑溪椎,檢查路徑是否在白名單中
進(jìn)程路徑是/proc/<進(jìn)程id>/exe指向的路徑
/
static int get_sender_path(pid_t pid, char
path)
{
int r;
sd_bus_creds *creds = NULL;
ssize_t len, buflen;

char exe_path[256] = {0};

snprintf(exe_path, 256, "/proc/%d/exe", pid);

len = readlink(exe_path, path, 256);

if( len < 1 )
{
    return -1;
}

return len;

}

static int check_path(char* path)
{
return has_in_whitelist(path);
}

/* 從sender的bus message獲取pid
調(diào)用者信息中包含pid等信息,找到相關(guān)接口獲取
*/
static pid_t get_sender_pid(sd_bus_message *m)
{
int r;
pid_t pid = -1;
sd_bus_creds *creds = NULL;

r = sd_bus_query_sender_creds(m, SD_BUS_CREDS_PID, &creds);
if (r < 0)
{
    fprintf(stderr, "sd_bus_query_sender_creds failed: %s, creds = %p\n", strerror(-r), creds);
    return 0;
}

r = sd_bus_creds_get_pid(creds, &pid);
if (r < 0)
{
    fprintf(stderr, "sd_bus_creds_get_pid failed: %s, creds = %p\n ", strerror(-r), creds);
    return 0;
}

fprintf(stderr, "sd_bus_creds_get_pid: %d\n", pid);

return pid;

}

/* 調(diào)用org.freedesktop.PolicyKit1.Authority dbus 進(jìn)行polkit鑒權(quán)
如果沒有權(quán)限 通過polkit agent與用戶交互恬口,獲取授權(quán)
*/
static int check_pid(pid_t pid)
{
sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus_message *m = NULL;
sd_bus *bus = NULL;
const char *path;
int r;

unsigned is_authorized = 0;
unsigned is_challenge = 0;
int detail_size;
int a;
const char* s1;
const char* s2;

/*獲取一個(gè)系統(tǒng)bus connection  */
r = sd_bus_open_system(&bus);
if (r < 0) 
{
    fprintf(stderr, "Failed to connect to system bus: %s\n", strerror(-r));
    goto finish;
}

/* 調(diào)用系統(tǒng)dbus接口方法 */
r = sd_bus_call_method(
    bus,
    "org.freedesktop.PolicyKit1",             /* service to contact */
    "/org/freedesktop/PolicyKit1/Authority",  /* object path */
    "org.freedesktop.PolicyKit1.Authority",   /* interface name */
    "CheckAuthorization",                     /* method name */
    &error,              /* object to return error in */
    &m,                  /* return message on success */
    "(sa{sv})sa{ss}us",  
        /* 后面參數(shù)的類型 
        CheckAuthorization (Struct of (String, Dict of {String, Variant}) subject, String action_id, Dict of {String, String} details, UInt32 flags, String cancellation_id)    
        
        struct 就用() 擴(kuò)起來
        Dict 用{} 括起來校读, 后面需要多加一個(gè)size值
        Variant ‘v’,需要加一個(gè)type
        可以對(duì)照著d-feet的method 參數(shù)填寫
        詳見https://manpages.debian.org/testing/libsystemd-dev/sd_bus_message_read.3.en.html
        */       
    "unix-process",     
    2,      // dict的個(gè)數(shù)祖能,即{sv}的個(gè)數(shù)
    "pid",
    "u",    // Variant的type int64
    pid,    // Variant的value
    "start-time",
    "t",    // glib vriant的type uint64
    0,      
    "net.poettering.Calculator", // 權(quán)限action名稱
    0,  // dict的個(gè)數(shù)歉秫,即{ss}的個(gè)數(shù)
    0x00000001,  /* 
        一般設(shè)置為1,表示如果pid沒有權(quán)限养铸,則嘗試通過polkit提權(quán) 
        The AuthorityFeatures Flags
        {
            None                 = 0x00000000,
            AllowUserInteraction = 0x00000001
        }
        Flags used in the CheckAuthorization() method.

        None : No flags set.

        AllowUserInteraction : If the Subject can obtain the authorization through authentication, and an authentication agent is available, then attempt to do so. Note, this means that the CheckAuthorization() method will block while the user is being asked to authenticate.
    */
    "" //cancellation_id雁芙,一般不用,設(shè)置為空
    );

if (r < 0) 
{
    fprintf(stderr, "Failed to Authority: %s\n", error.message);
    goto finish;
}

/*  讀取dubs信息  
    (Struct of (Boolean, Boolean, Dict of {String, String}) result)
    因?yàn)閐ict是變長的钞螟,需要循環(huán)讀取
    首先進(jìn)入結(jié)構(gòu)體(直接讀取會(huì)返回失斖酶省)

    systemd-241.8/src/systemd/sd-bus-protocol.h 

    SD_BUS_TYPE_BYTE             = 'y',
    SD_BUS_TYPE_BOOLEAN          = 'b',
    SD_BUS_TYPE_INT16            = 'n',
    SD_BUS_TYPE_UINT16           = 'q',
    SD_BUS_TYPE_INT32            = 'i',
    SD_BUS_TYPE_UINT32           = 'u',
    SD_BUS_TYPE_INT64            = 'x',
    SD_BUS_TYPE_UINT64           = 't',
    SD_BUS_TYPE_DOUBLE           = 'd',
    SD_BUS_TYPE_STRING           = 's',
    SD_BUS_TYPE_OBJECT_PATH      = 'o',
    SD_BUS_TYPE_SIGNATURE        = 'g',
    SD_BUS_TYPE_UNIX_FD          = 'h',
    SD_BUS_TYPE_ARRAY            = 'a',
    SD_BUS_TYPE_VARIANT          = 'v',
    SD_BUS_TYPE_STRUCT           = 'r',
    SD_BUS_TYPE_STRUCT_BEGIN     = '(',
    SD_BUS_TYPE_STRUCT_END       = ')',
    SD_BUS_TYPE_DICT_ENTRY       = 'e',
    SD_BUS_TYPE_DICT_ENTRY_BEGIN = '{',
    SD_BUS_TYPE_DICT_ENTRY_END   = '}'
*/

/*讀取調(diào)用結(jié)果
*/
r = sd_bus_message_enter_container(m, 'r', "bba{ss}");
if (r < 0)
{
    printf("sd_bus_message_enter_container failed.\n");
    goto finish;
}

//讀取前兩個(gè)bool
r = sd_bus_message_read(m, "bb", &is_authorized, &is_challenge);
if( r < 0)
{
    printf("sd_bus_message_read failed.\n");
    goto finish;
}

printf("sd_bus_message_read is_authorized = %s, is_challenge = %s",is_authorized?"true":"false"\
        ,is_challenge?"true":"false");
//
r = sd_bus_message_enter_container(m, 'a', "{ss}");

// 變長數(shù)組循環(huán)讀取
while(1)
{
    r = sd_bus_message_read(m, "{ss}", &s1, &s2);
    if (r <= 0) {
        break;
    }

    printf(" %s=%s", s1, s2);
}

sd_bus_message_exit_container(m);
printf(".\n");

r = sd_bus_message_exit_container(m);

finish:
/*關(guān)閉系統(tǒng)dbus連接
*/
sd_bus_error_free(&error);
sd_bus_message_unref(m);
sd_bus_unref(bus);

if( r > 0 && !is_authorized )
{
    return -1;
}

return r;

}

/*
檢測sender的權(quán)限
如果sender path在白名單中 返回成功
如果不在白名單 則polkit鑒權(quán)
*/
static int check_auth(sd_bus_message *m)
{
int r;
char path[256] = {0};

pid_t pid = get_sender_pid(m);

if(pid < 0)
{
    fprintf(stderr, "get_sender_pid failed. pid = %d \n", pid);
    return pid;
}

r = get_sender_path(pid, path);
if(r > 0)
{
    r = check_path(path);
    fprintf(stderr, "check_path result. r = %d \n", r);
}

if(r > 0)
{
    return r;
}

r = check_pid(pid);

return r;

}

/* dbus method調(diào)用在這里被處理
*/
static int method_multiply(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
{
int64_t x, y;
int r;

/*
在調(diào)用方法前進(jìn)行白名單驗(yàn)證和polkit鑒權(quán)
失敗直接返回錯(cuò)誤
*/
r = check_auth(m);
if( r < 0)
{
    fprintf(stderr, "check_auth failed: %s\n", strerror(-r));
    return sd_bus_reply_method_return(m, "xs", 0, &"auth failed");
}

/* 讀取傳入?yún)?shù) */
r = sd_bus_message_read(m, "xx", &x, &y);
if (r < 0) 
{
    fprintf(stderr, "Failed to parse parameters: %s\n", strerror(-r));
    return r;
}

/* 返回dbus結(jié)果 */
return sd_bus_reply_method_return(m, "xs", x * y, &"success");

}

/* vtable定義dubs object的方法、信號(hào)鳞滨、屬性等
這里定義了一個(gè)乘法的method
*/
static const sd_bus_vtable calculator_vtable[] =
{
SD_BUS_VTABLE_START(0),
SD_BUS_METHOD("Multiply", "xx", "xs", method_multiply, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_VTABLE_END
};

int main(int argc, char *argv[])
{
sd_bus_slot *slot = NULL;
sd_bus *bus = NULL;
int r;

/* 1. 獲取一個(gè)系統(tǒng)bus connection 
*/
r = sd_bus_open_system(&bus);
if (r < 0) 
{
    fprintf(stderr, "Failed to connect to system bus: %s\n", strerror(-r));
    goto finish;
}

/* 2. 安裝bus object洞焙,設(shè)置 bus路徑和接口,綁定bus 方法
 */
r = sd_bus_add_object_vtable(bus,
                            &slot,
                            "/net/poettering/Calculator",  /* object path */
                            "net.poettering.Calculator",   /* interface name */
                            calculator_vtable,
                            NULL);
if (r < 0) 
{
    fprintf(stderr, "Failed to issue method call: %s\n", strerror(-r));
    goto finish;
}

/* 3. 設(shè)置服務(wù)名稱 供客戶查找和調(diào)用
*/
r = sd_bus_request_name(bus, "net.poettering.Calculator", 0);
if (r < 0) 
{
    fprintf(stderr, "Failed to acquire service name: %s\n", strerror(-r));
    goto finish;
}

/*
   4. 循環(huán)等待并處理bus請(qǐng)求事件 
*/
while(1) 
{
    /* 處理bus請(qǐng)求 實(shí)際的流程在方法中處理 
    */
    r = sd_bus_process(bus, NULL);
    if (r < 0) 
    {
        fprintf(stderr, "Failed to process bus: %s\n", strerror(-r));
        goto finish;
    }

    if (r > 0)
        continue;

    /* 等待下一個(gè)事件 */
    r = sd_bus_wait(bus, (uint64_t) -1);
    if (r < 0) 
    {
        fprintf(stderr, "Failed to wait on bus: %s\n", strerror(-r));
        goto finish;
    }
}

finish:
/* 服務(wù)退出時(shí) 關(guān)閉獲取到的系統(tǒng)bus連接
*/
sd_bus_slot_unref(slot);
sd_bus_unref(bus);

return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;

}
</code></pre>
</details>

(1) 獲取系統(tǒng)dbus
使用sd_bus_open_system接口拿到系統(tǒng)dbus.
(2) 在系統(tǒng)dbus中安裝自定義的dbus object,設(shè)置D-Bus路徑拯啦,接口澡匪,方法
這立我們創(chuàng)建了一個(gè)method_multiply乘法方法,它的路徑為/net/poettering/Calculator褒链,接口為net.poettering.Calculator.
(3) 如果調(diào)用method_multiply方法唁情,這里需要進(jìn)行鑒權(quán).
①首先獲取請(qǐng)求dbus的進(jìn)程pid
sd-dbus中的獲取方法見 test.c get_sender_pid(sd_bus_message m)函數(shù).
②接著檢查進(jìn)程的路徑是否在白名單中.
根據(jù)進(jìn)程pid獲取進(jìn)程的路徑,具體實(shí)現(xiàn)參考 test.c check_path(char
path)函數(shù).
檢查進(jìn)程path是否在白名單文件中甫匹,具體參考 test.c check_path(char* path)函數(shù).
若進(jìn)程路徑在白名單中荠瘪,則驗(yàn)證成功.
③polkit鑒權(quán)
調(diào)用org.freedesktop.PolicyKit1.Authority dbus接口夯巷,對(duì)進(jìn)程進(jìn)行認(rèn)證.
sender進(jìn)程如果擁有action的權(quán)限,認(rèn)證通過.
sender進(jìn)程如果沒有action的權(quán)限哀墓,將調(diào)用polkit彈框與用戶交互獲取授權(quán).
具體實(shí)現(xiàn)參考test.c check_pid(pid_t pid)函數(shù).
sender進(jìn)程如果具備權(quán)限或交互獲取到了權(quán)限趁餐,則驗(yàn)證成功.
④未通過驗(yàn)證的dbus請(qǐng)求,返回失敗.
(4)設(shè)置服務(wù)名稱 供客戶查找和調(diào)用

sd_bus_request_name(bus, "net.poettering.Calculator", 0)    

(5)循環(huán)等待并處理bus請(qǐng)求事件
(6)服務(wù)退出時(shí) 關(guān)閉獲取到的系統(tǒng)bus連接
(7)編譯 test.c 使用make或

gcc test.c -o test `pkg-config --cflags --libs libsystemd`

(8)由于是系統(tǒng)D-Bus篮绰,所以程序需要使用管理員權(quán)限運(yùn)行

sudo ./test 

(9)D-Feet工具檢查net.poettering.Calculator是否啟用

你也可以使用Qt Dbus后雷、dbus-glib或其他dbus庫創(chuàng)建自己的dbus接口.

4. 客戶端調(diào)用D-Bus接口

<details>
<summary>展開查看 代碼test_client.c</summary>
<pre>

include <stdio.h>

include <stdlib.h>

include <errno.h>

include <systemd/sd-bus.h>

int main()
{
sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus_message *m = NULL;
sd_bus *bus = NULL;
const char *path;
int r;
</pre>
<code>

uint64_t result;
const char* s;

/* 1. 獲取一個(gè)系統(tǒng)bus連接
    如果是用戶dbus接口,使用 sd_bus_open_user 函數(shù) 獲取 用戶dbus連接
 */
r = sd_bus_open_system(&bus);
if (r < 0) 
{
    fprintf(stderr, "Failed to connect to system bus: %s\n", strerror(-r));
    goto finish;
}

/* 2. 調(diào)用dbus接口方法 */
r = sd_bus_call_method(
    bus,
    "net.poettering.Calculator",   /* dubs 名稱*/
    "/net/poettering/Calculator",  /* dbus結(jié)構(gòu)體路徑 */
    "net.poettering.Calculator",   /* 接口名稱 */
    "Multiply",                    /* 方法 */
    &error,                        /* 返回錯(cuò)誤信息保存 */
    &m,                            /* return message on success */
    "xx", 
        /* 輸入?yún)?shù)類型 ’xx‘ 表示接下來的兩個(gè)int64參數(shù)
        結(jié)構(gòu)體用 () 
        disct用 {}
        glibvariant 用v標(biāo)示
        可以對(duì)照著d-feet的method 參數(shù)填寫
        詳見 https://manpages.debian.org/testing/libsystemd-dev/sd_bus_message_read.3.en.html
        */
    2,
    3
    );

if (r < 0)
{
    fprintf(stderr, "Failed to issue method call: %s\n", error.message);
    goto finish;
}
/* 3.讀取結(jié)果
*/
r = sd_bus_message_read(m, "xs", &result, &s);
if( r < 0)
{
    printf("sd_bus_message_read failed.\n");
    goto finish;
}

printf("sd_bus_message_read result = %s, %u.\n",s, result);

finish:
/*釋放dbus連接
*/
sd_bus_error_free(&error);
sd_bus_message_unref(m);
sd_bus_unref(bus);

return r;

}
</code>

</details>
運(yùn)行此客戶端程序吠各,將彈出polkit鑒權(quán)窗口臀突,只有當(dāng)輸入正確密碼方可執(zhí)行。

三贾漏、 dbus接口使用polkit鑒權(quán)應(yīng)用場景

當(dāng)提供的服務(wù)候学、接口涉及安全,對(duì)調(diào)用者身份有驗(yàn)證要求時(shí)推薦使用這種方式.
以系統(tǒng)dbus中進(jìn)行白名單檢查纵散、polkit鑒權(quán)的方式提供服務(wù)梳码,保證了安全性.
調(diào)用者使用接口,不需要再單獨(dú)進(jìn)行鑒權(quán)等安全認(rèn)證操作.

The new sd-bus API of systemd
polkit Reference Manual
Creating a D-Bus Service with dbus-python and Polkit Authentication
sd_bus_call_method Examples
sd-bus

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末伍掀,一起剝皮案震驚了整個(gè)濱河市掰茶,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蜜笤,老刑警劉巖濒蒋,帶你破解...
    沈念sama閱讀 216,744評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異把兔,居然都是意外死亡沪伙,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門县好,熙熙樓的掌柜王于貴愁眉苦臉地迎上來围橡,“玉大人,你說我怎么就攤上這事聘惦。” “怎么了儒恋?”我有些...
    開封第一講書人閱讀 163,105評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵善绎,是天一觀的道長。 經(jīng)常有香客問我诫尽,道長禀酱,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,242評(píng)論 1 292
  • 正文 為了忘掉前任牧嫉,我火速辦了婚禮剂跟,結(jié)果婚禮上减途,老公的妹妹穿的比我還像新娘。我一直安慰自己曹洽,他們只是感情好鳍置,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,269評(píng)論 6 389
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著送淆,像睡著了一般税产。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上偷崩,一...
    開封第一講書人閱讀 51,215評(píng)論 1 299
  • 那天辟拷,我揣著相機(jī)與錄音,去河邊找鬼阐斜。 笑死衫冻,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的谒出。 我是一名探鬼主播隅俘,決...
    沈念sama閱讀 40,096評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼到推!你這毒婦竟也來了考赛?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,939評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤莉测,失蹤者是張志新(化名)和其女友劉穎颜骤,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體捣卤,經(jīng)...
    沈念sama閱讀 45,354評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡忍抽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,573評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了董朝。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鸠项。...
    茶點(diǎn)故事閱讀 39,745評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖子姜,靈堂內(nèi)的尸體忽然破棺而出祟绊,到底是詐尸還是另有隱情,我是刑警寧澤哥捕,帶...
    沈念sama閱讀 35,448評(píng)論 5 344
  • 正文 年R本政府宣布牧抽,位于F島的核電站,受9級(jí)特大地震影響遥赚,放射性物質(zhì)發(fā)生泄漏扬舒。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,048評(píng)論 3 327
  • 文/蒙蒙 一凫佛、第九天 我趴在偏房一處隱蔽的房頂上張望讲坎。 院中可真熱鬧孕惜,春花似錦、人聲如沸晨炕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,683評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽府瞄。三九已至碧磅,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間遵馆,已是汗流浹背鲸郊。 一陣腳步聲響...
    開封第一講書人閱讀 32,838評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留货邓,地道東北人秆撮。 一個(gè)月前我還...
    沈念sama閱讀 47,776評(píng)論 2 369
  • 正文 我出身青樓,卻偏偏與公主長得像换况,于是被迫代替她去往敵國和親职辨。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,652評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容