Coverage Report

Created: 2026-07-12 07:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/epan/dissectors/packet-pcnfsd.c
Line
Count
Source
1
/* packet-pcnfsd.c
2
 * Routines for PCNFSD dissection
3
 *
4
 * Wireshark - Network traffic analyzer
5
 * By Gerald Combs <gerald@wireshark.org>
6
 * Copyright 1998 Gerald Combs
7
 *
8
 * Copied from packet-ypbind.c
9
 *
10
 * SPDX-License-Identifier: GPL-2.0-or-later
11
 */
12
13
14
/*
15
Protocol information comes from the book
16
    "NFS Illustrated" by Brent Callaghan, ISBN 0-201-32570-5
17
*/
18
19
20
#include "config.h"
21
22
23
#include "packet-rpc.h"
24
#include "packet-pcnfsd.h"
25
26
void proto_register_pcnfsd(void);
27
void proto_reg_handoff_pcnfsd(void);
28
29
static int proto_pcnfsd;
30
static int hf_pcnfsd_procedure_v1;
31
static int hf_pcnfsd_procedure_v2;
32
static int hf_pcnfsd_auth_client;
33
static int hf_pcnfsd_auth_ident_obscure;
34
static int hf_pcnfsd_auth_ident_clear;
35
static int hf_pcnfsd_auth_password;
36
static int hf_pcnfsd_auth_password_obscure;
37
static int hf_pcnfsd_auth_password_clear;
38
static int hf_pcnfsd_comment;
39
static int hf_pcnfsd_status;
40
static int hf_pcnfsd_uid;
41
static int hf_pcnfsd_gid;
42
static int hf_pcnfsd_gids_count;
43
static int hf_pcnfsd_homedir;
44
static int hf_pcnfsd_def_umask;
45
static int hf_pcnfsd_mapreq;
46
static int hf_pcnfsd_mapreq_status;
47
static int hf_pcnfsd_username;
48
49
50
static int ett_pcnfsd;
51
static int ett_pcnfsd_auth_ident;
52
static int ett_pcnfsd_auth_password;
53
static int ett_pcnfsd_gids;
54
55
static int
56
dissect_pcnfsd_username(tvbuff_t *tvb, packet_info* pinfo, unsigned offset, proto_tree *tree)
57
0
{
58
0
    return dissect_rpc_string(tvb, pinfo, tree, hf_pcnfsd_username, offset, NULL);
59
0
}
60
61
#define MAP_REQ_UID     0
62
#define MAP_REQ_GID     1
63
#define MAP_REQ_UNAME   2
64
#define MAP_REQ_GNAME   3
65
66
static const value_string names_mapreq[] =
67
{
68
    { MAP_REQ_UID,   "MAP_REQ_UID"   },
69
    { MAP_REQ_GID,   "MAP_REQ_GID"   },
70
    { MAP_REQ_UNAME, "MAP_REQ_UNAME" },
71
    { MAP_REQ_GNAME, "MAP_REQ_GNAME" },
72
    { 0,  NULL    }
73
};
74
75
static int
76
dissect_pcnfsd2_dissect_mapreq_arg_item(tvbuff_t *tvb, unsigned offset, packet_info *pinfo, proto_tree *tree, void* data _U_)
77
0
{
78
0
    proto_tree_add_item(tree, hf_pcnfsd_mapreq, tvb, offset, 4, ENC_BIG_ENDIAN);
79
0
    offset += 4;
80
81
0
    offset = dissect_rpc_uint32(tvb, tree, hf_pcnfsd_uid, offset);
82
83
0
    offset = dissect_pcnfsd_username(tvb, pinfo, offset, tree);
84
85
0
    return offset;
86
0
}
87
88
static int
89
dissect_pcnfsd2_mapid_call(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
90
0
{
91
0
    unsigned offset = 0;
92
0
    offset = dissect_rpc_string(tvb, pinfo, tree, hf_pcnfsd_comment, offset, NULL);
93
94
0
    offset = dissect_rpc_list(tvb, pinfo, tree, offset,
95
0
          dissect_pcnfsd2_dissect_mapreq_arg_item, NULL);
96
97
0
    return offset;
98
0
}
99
100
#define MAP_RES_OK      0
101
#define MAP_RES_UNKNOWN 1
102
#define MAP_RES_DENIED  2
103
104
static const value_string names_maprstat[] =
105
{
106
    { MAP_RES_OK,      "MAP_RES_OK" },
107
    { MAP_RES_UNKNOWN, "MAP_RES_UNKNOWN"    },
108
    { MAP_RES_DENIED,  "MAP_RES_DENIED" },
109
    { 0,    NULL    }
110
};
111
112
static int
113
dissect_pcnfsd2_dissect_mapreq_res_item(tvbuff_t *tvb, unsigned offset, packet_info *pinfo, proto_tree *tree, void* data _U_)
114
0
{
115
0
    proto_tree_add_item(tree, hf_pcnfsd_mapreq, tvb, offset, 4, ENC_BIG_ENDIAN);
116
0
    offset += 4;
117
118
0
    proto_tree_add_item(tree, hf_pcnfsd_mapreq_status, tvb, offset, 4, ENC_BIG_ENDIAN);
119
0
    offset += 4;
120
121
0
    offset = dissect_rpc_uint32(tvb, tree, hf_pcnfsd_uid, offset);
122
123
0
    offset = dissect_pcnfsd_username(tvb, pinfo, offset, tree);
124
125
0
    return offset;
126
0
}
127
128
static int
129
dissect_pcnfsd2_mapid_reply(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
130
0
{
131
0
    unsigned offset = 0;
132
0
    offset = dissect_rpc_string(tvb, pinfo, tree, hf_pcnfsd_comment, offset, NULL);
133
134
0
    offset = dissect_rpc_list(tvb, pinfo, tree, offset,
135
0
          dissect_pcnfsd2_dissect_mapreq_res_item, NULL);
136
137
0
    return offset;
138
0
}
139
140
/* "NFS Illustrated 14.7.13 */
141
static wmem_strbuf_t*
142
pcnfsd_decode_obscure(wmem_allocator_t *pool, const char* data, size_t len)
143
0
{
144
0
    wmem_strbuf_t *decoded_buf;
145
146
0
    decoded_buf = wmem_strbuf_new_sized(pool, len + 1);
147
0
    for ( ; len>0 ; len--, data++) {
148
        /* dissect_rpc_string/dissect_rpc_opaque_data already called
149
         * tvb_get_string_enc(..., ENC_ASCII), which replaced any non-ASCII
150
         * with UTF-8 REPLACEMENT CHARACTER, so leave unchanged any byte
151
         * with the high bit set.
152
         * XXX - It might be easier to get the raw bytes and deobfuscate
153
         * prior to making UTF-8.
154
         */
155
0
        if (*data & 0x80) {
156
0
            wmem_strbuf_append_c(decoded_buf, *data);
157
0
        } else {
158
0
            wmem_strbuf_append_c(decoded_buf, *data ^ 0x5b);
159
0
        }
160
0
    }
161
    /* Return the wmem_strbuf_t for clarity, since the deobfuscation can
162
     * place an internal '\0' byte. (We could just use the original string
163
     * length, but this makes it more apparent.) */
164
0
    return decoded_buf;
165
0
}
166
167
168
/* "NFS Illustrated" 14.7.13 */
169
static int
170
dissect_pcnfsd2_auth_call(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
171
0
{
172
0
    unsigned    newoffset;
173
0
    const char *ident         = NULL;
174
0
    wmem_strbuf_t *ident_decoded;
175
0
    proto_item *ident_item;
176
0
    proto_tree *ident_tree;
177
0
    const char *password      = NULL;
178
0
    wmem_strbuf_t *password_decoded;
179
0
    proto_item *password_item = NULL;
180
0
    proto_tree *password_tree = NULL;
181
0
    unsigned offset = 0;
182
183
0
    offset = dissect_rpc_string(tvb, pinfo, tree,
184
0
        hf_pcnfsd_auth_client, offset, NULL);
185
186
0
    ident_tree = proto_tree_add_subtree(tree, tvb,
187
0
                offset, -1, ett_pcnfsd_auth_ident, &ident_item, "Authentication Ident");
188
189
    /* XXX - Because this is obfuscated with a XOR pattern, it really needs
190
     * to retrieve the raw bytes and XOR them first; the obfuscation can place
191
     * a '\0' in the middle of the string if the unobfuscated identity or
192
     * password has a '[' (ASCII 0x5b) , so the counted string length in the
193
     * frame should be used, not strlen().
194
     */
195
0
    newoffset = dissect_rpc_string(tvb, pinfo, ident_tree,
196
0
        hf_pcnfsd_auth_ident_obscure, offset, &ident);
197
0
    proto_item_set_len(ident_item, newoffset-offset);
198
199
0
    if (ident && ident_tree) {
200
        /* Only attempt to decode the ident if it has been specified */
201
0
        if (strcmp(ident, RPC_STRING_EMPTY) != 0)
202
0
            ident_decoded = pcnfsd_decode_obscure(pinfo->pool, ident, strlen(ident));
203
0
        else
204
0
            ident_decoded = wmem_strbuf_new(pinfo->pool, ident);
205
206
0
        proto_tree_add_string(ident_tree,
207
0
            hf_pcnfsd_auth_ident_clear,
208
0
            tvb, offset+4,
209
0
            (int)wmem_strbuf_get_len(ident_decoded),
210
0
            wmem_strbuf_get_str(ident_decoded));
211
212
0
        wmem_strbuf_destroy(ident_decoded);
213
0
    }
214
0
    if (ident_item) {
215
0
        proto_item_set_text(ident_item, "Authentication Ident: %s",
216
0
            ident);
217
0
    }
218
219
0
    offset = newoffset;
220
221
0
    password_item = proto_tree_add_string_format(tree, hf_pcnfsd_auth_password,
222
0
                                          tvb, 0, 0, "", "Authentication Password");
223
0
    password_tree = proto_item_add_subtree(password_item, ett_pcnfsd_auth_password);
224
225
0
    newoffset = dissect_rpc_string(tvb, pinfo, password_tree,
226
0
        hf_pcnfsd_auth_password_obscure, offset, &password);
227
0
    if (password_item) {
228
0
        proto_item_set_len(password_item, newoffset-offset);
229
0
    }
230
231
0
    if (password && password_tree) {
232
        /* Only attempt to decode the password if it has been specified */
233
0
        if (strcmp(password, RPC_STRING_EMPTY))
234
0
            password_decoded = pcnfsd_decode_obscure(pinfo->pool, password, strlen(password));
235
0
        else
236
0
            password_decoded = wmem_strbuf_new(pinfo->pool, password);
237
238
0
        proto_tree_add_string(password_tree,
239
0
            hf_pcnfsd_auth_password_clear,
240
0
            tvb, offset+4,
241
0
            (int)wmem_strbuf_get_len(password_decoded),
242
0
            wmem_strbuf_get_str(password_decoded));
243
244
0
        wmem_strbuf_destroy(password_decoded);
245
0
    }
246
0
    if (password_item) {
247
0
        proto_item_set_text(password_item, "Authentication Password: %s",
248
0
            password);
249
0
    }
250
251
0
    offset = newoffset;
252
253
0
    offset = dissect_rpc_string(tvb, pinfo, tree,
254
0
        hf_pcnfsd_comment, offset, NULL);
255
256
0
    return offset;
257
0
}
258
259
260
/* "NFS Illustrated" 14.7.13 */
261
static int
262
dissect_pcnfsd2_auth_reply(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
263
0
{
264
0
    int         gids_count;
265
0
    proto_tree *gtree;
266
0
    int         gids_i;
267
0
    unsigned offset = 0;
268
269
0
    offset = dissect_rpc_uint32(tvb, tree, hf_pcnfsd_status, offset);
270
0
    offset = dissect_rpc_uint32(tvb, tree, hf_pcnfsd_uid, offset);
271
0
    offset = dissect_rpc_uint32(tvb, tree, hf_pcnfsd_gid, offset);
272
0
    gids_count = tvb_get_ntohl(tvb,offset+0);
273
0
    gtree = proto_tree_add_subtree_format(tree, tvb,
274
0
            offset, 4+gids_count*4, ett_pcnfsd_gids, NULL, "Group IDs: %d", gids_count);
275
276
0
    proto_tree_add_item(gtree, hf_pcnfsd_gids_count, tvb, offset, 4, ENC_BIG_ENDIAN);
277
278
0
    offset += 4;
279
0
    for (gids_i = 0 ; gids_i < gids_count ; gids_i++) {
280
0
        offset = dissect_rpc_uint32(tvb, gtree,
281
0
                hf_pcnfsd_gid, offset);
282
0
    }
283
0
    offset = dissect_rpc_string(tvb, pinfo, tree,
284
0
        hf_pcnfsd_homedir, offset, NULL);
285
    /* should be signed int32 */
286
0
    offset = dissect_rpc_uint32(tvb, tree, hf_pcnfsd_def_umask, offset);
287
0
    offset = dissect_rpc_string(tvb, pinfo, tree,
288
0
        hf_pcnfsd_comment, offset, NULL);
289
290
0
    return offset;
291
0
}
292
293
294
/* "NFS Illustrated", 14.6 */
295
/* proc number, "proc name", dissect_request, dissect_reply */
296
static const vsff pcnfsd1_proc[] = {
297
    { 0,    "NULL",     dissect_rpc_void,    dissect_rpc_void },
298
    { 1,    "AUTH",     dissect_rpc_unknown, dissect_rpc_unknown },
299
    { 2,    "PR_INIT",  dissect_rpc_unknown, dissect_rpc_unknown },
300
    { 3,    "PR_START", dissect_rpc_unknown, dissect_rpc_unknown },
301
    { 0,    NULL,       NULL,                NULL }
302
};
303
static const value_string pcnfsd1_proc_vals[] = {
304
    { 0,    "NULL" },
305
    { 1,    "AUTH" },
306
    { 2,    "PR_INIT" },
307
    { 3,    "PR_START" },
308
    { 0,    NULL }
309
};
310
/* end of PCNFS version 1 */
311
312
313
/* "NFS Illustrated", 14.7 */
314
static const vsff pcnfsd2_proc[] = {
315
    {  0,   "NULL",
316
    dissect_rpc_void, dissect_rpc_void },
317
    {  1,   "INFO",       dissect_rpc_unknown, dissect_rpc_unknown },
318
    {  2,   "PR_INIT",    dissect_rpc_unknown, dissect_rpc_unknown },
319
    {  3,   "PR_START",   dissect_rpc_unknown, dissect_rpc_unknown },
320
    {  4,   "PR_LIST",    dissect_rpc_unknown, dissect_rpc_unknown },
321
    {  5,   "PR_QUEUE",   dissect_rpc_unknown, dissect_rpc_unknown },
322
    {  6,   "PR_STATUS",  dissect_rpc_unknown, dissect_rpc_unknown },
323
    {  7,   "PR_CANCEL",  dissect_rpc_unknown, dissect_rpc_unknown },
324
    {  8,   "PR_ADMIN",   dissect_rpc_unknown, dissect_rpc_unknown },
325
    {  9,   "PR_REQUEUE", dissect_rpc_unknown, dissect_rpc_unknown },
326
    { 10,   "PR_HOLD",    dissect_rpc_unknown, dissect_rpc_unknown },
327
    { 11,   "PR_RELEASE", dissect_rpc_unknown, dissect_rpc_unknown },
328
    { 12,   "MAPID",
329
    dissect_pcnfsd2_mapid_call, dissect_pcnfsd2_mapid_reply },
330
    { 13,   "AUTH",
331
    dissect_pcnfsd2_auth_call,  dissect_pcnfsd2_auth_reply },
332
    { 14,   "ALERT",      dissect_rpc_unknown, dissect_rpc_unknown },
333
    { 0,    NULL,         NULL,               NULL }
334
};
335
static const value_string pcnfsd2_proc_vals[] = {
336
    {  0,   "NULL" },
337
    {  1,   "INFO" },
338
    {  2,   "PR_INIT" },
339
    {  3,   "PR_START" },
340
    {  4,   "PR_LIST" },
341
    {  5,   "PR_QUEUE" },
342
    {  6,   "PR_STATUS" },
343
    {  7,   "PR_CANCEL" },
344
    {  8,   "PR_ADMIN" },
345
    {  9,   "PR_REQUEUE" },
346
    { 10,   "PR_HOLD" },
347
    { 11,   "PR_RELEASE" },
348
    { 12,   "MAPID" },
349
    { 13,   "AUTH" },
350
    { 14,   "ALERT" },
351
    { 0,    NULL }
352
};
353
/* end of PCNFS version 2 */
354
355
static const rpc_prog_vers_info pcnfsd_vers_info[] = {
356
  { 1, pcnfsd1_proc, &hf_pcnfsd_procedure_v1 },
357
  { 2, pcnfsd2_proc, &hf_pcnfsd_procedure_v2 },
358
};
359
360
void
361
proto_register_pcnfsd(void)
362
15
{
363
15
    static hf_register_info hf[] = {
364
15
        { &hf_pcnfsd_procedure_v1, {
365
15
                "V1 Procedure", "pcnfsd.procedure_v1", FT_UINT32, BASE_DEC,
366
15
                VALS(pcnfsd1_proc_vals), 0, NULL, HFILL }},
367
15
        { &hf_pcnfsd_procedure_v2, {
368
15
                "V2 Procedure", "pcnfsd.procedure_v2", FT_UINT32, BASE_DEC,
369
15
                VALS(pcnfsd2_proc_vals), 0, NULL, HFILL }},
370
15
        { &hf_pcnfsd_auth_client, {
371
15
                "Authentication Client", "pcnfsd.auth.client", FT_STRING, BASE_NONE,
372
15
                NULL, 0, NULL, HFILL }},
373
15
        { &hf_pcnfsd_auth_ident_obscure, {
374
15
                "Obscure Ident", "pcnfsd.auth.ident.obscure", FT_STRING, BASE_NONE,
375
15
                NULL, 0, "Authentication Obscure Ident", HFILL }},
376
15
        { &hf_pcnfsd_auth_ident_clear, {
377
15
                "Clear Ident", "pcnfsd.auth.ident.clear", FT_STRING, BASE_NONE,
378
15
                NULL, 0, "Authentication Clear Ident", HFILL }},
379
15
        { &hf_pcnfsd_auth_password, {
380
15
                "Password", "pcnfsd.auth.password", FT_STRING, BASE_NONE,
381
15
                NULL, 0, NULL, HFILL }},
382
15
        { &hf_pcnfsd_auth_password_obscure, {
383
15
                "Obscure Password", "pcnfsd.auth.password.obscure", FT_STRING, BASE_NONE,
384
15
                NULL, 0, "Authentication Obscure Password", HFILL }},
385
15
        { &hf_pcnfsd_auth_password_clear, {
386
15
                "Clear Password", "pcnfsd.auth.password.clear", FT_STRING, BASE_NONE,
387
15
                NULL, 0, "Authentication Clear Password", HFILL }},
388
15
        { &hf_pcnfsd_comment, {
389
15
                "Comment", "pcnfsd.comment", FT_STRING, BASE_NONE,
390
15
                NULL, 0, NULL, HFILL }},
391
15
        { &hf_pcnfsd_status, {
392
15
                "Reply Status", "pcnfsd.status", FT_UINT32, BASE_DEC,
393
15
                NULL, 0, NULL, HFILL }},
394
15
        { &hf_pcnfsd_uid, {
395
15
                "User ID", "pcnfsd.uid", FT_UINT32, BASE_DEC,
396
15
                NULL, 0, NULL, HFILL }},
397
15
        { &hf_pcnfsd_gid, {
398
15
                "Group ID", "pcnfsd.gid", FT_UINT32, BASE_DEC,
399
15
                NULL, 0, NULL, HFILL }},
400
15
        { &hf_pcnfsd_gids_count, {
401
15
                "Group ID Count", "pcnfsd.gids.count", FT_UINT32, BASE_DEC,
402
15
                NULL, 0, NULL, HFILL }},
403
15
        { &hf_pcnfsd_homedir, {
404
15
                "Home Directory", "pcnfsd.homedir", FT_STRING, BASE_NONE,
405
15
                NULL, 0, NULL, HFILL }},
406
15
        { &hf_pcnfsd_def_umask, {
407
15
                "def_umask", "pcnfsd.def_umask", FT_UINT32, BASE_OCT,
408
15
                NULL, 0, NULL, HFILL }},
409
15
        { &hf_pcnfsd_mapreq, {
410
15
                "Request", "pcnfsd.mapreq", FT_UINT32, BASE_DEC,
411
15
                VALS(names_mapreq), 0, NULL, HFILL }},
412
15
        { &hf_pcnfsd_mapreq_status, {
413
15
                "Status", "pcnfsd.mapreq_status", FT_UINT32, BASE_DEC,
414
15
                VALS(names_maprstat), 0, NULL, HFILL }},
415
15
        { &hf_pcnfsd_username, {
416
15
                "User name", "pcnfsd.username", FT_STRING, BASE_NONE,
417
15
                NULL, 0, "pcnfsd.username", HFILL }},
418
15
    };
419
420
15
    static int *ett[] = {
421
15
        &ett_pcnfsd,
422
15
        &ett_pcnfsd_auth_ident,
423
15
        &ett_pcnfsd_auth_password,
424
15
        &ett_pcnfsd_gids
425
15
    };
426
427
15
    proto_pcnfsd = proto_register_protocol("PC NFS", "PCNFSD", "pcnfsd");
428
15
    proto_register_field_array(proto_pcnfsd, hf, array_length(hf));
429
15
    proto_register_subtree_array(ett, array_length(ett));
430
15
}
431
432
void
433
proto_reg_handoff_pcnfsd(void)
434
15
{
435
    /* Register the protocol as RPC */
436
15
    rpc_init_prog(proto_pcnfsd, PCNFSD_PROGRAM, ett_pcnfsd,
437
                  G_N_ELEMENTS(pcnfsd_vers_info), pcnfsd_vers_info);
438
15
}
439
440
/*
441
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
442
 *
443
 * Local variables:
444
 * c-basic-offset: 4
445
 * tab-width: 8
446
 * indent-tabs-mode: nil
447
 * End:
448
 *
449
 * vi: set shiftwidth=4 tabstop=8 expandtab:
450
 * :indentSize=4:tabSize=8:noTabs=true:
451
 */